Singularity
API Reference

Service API

Complete API reference for the service layer — Manager, Acquire, ServiceDiscovery, ServiceGenerator, BaseWebhook, and WebSocketManager.

This page provides structured API tables for every class in the Singularity service layer. All signatures are taken directly from the source code in singularity/core/ and singularity/common/websocket.py.


Manager

services.__base.manager.Manager

The Manager is the central orchestrator. It scans for services and middlewares, registers routers with FastAPI, initializes RPC, and manages the application lifespan (startup/shutdown).

Constructor

ParameterTypeDefaultDescription
appFastAPI(required)The FastAPI application instance
prefixstr"/api"Base URL prefix for all service routers

Attributes

AttributeTypeDescription
appFastAPIThe FastAPI application instance
prefixstrBase URL prefix for all services
acquireAcquireThe shared dependency injection container
services_dirstrAbsolute path to the services/ directory
mws_dirstrAbsolute path to the singularity/middleware/ directory
ws_routesDict[str, Type]Registered WebSocket route endpoints

Methods

MethodParametersReturn TypeDescription
register_services()(none)NoneRecursively scan services_dir, import each service.py, inject Acquire, register routers and RPC entries.
register_middlewares()(none)NoneScan mws_dir for middleware modules, instantiate each Middleware class, and add to the FastAPI app.
startup()(none)NoneAsync. Publishes the RPC spec to Redis, discovers remote services, and starts the heartbeat loop (30s interval, 60s TTL).
shutdown()(none)NoneAsync. Cancels the heartbeat task and removes RPC specs from Redis.
register_ws_routes(router, service_instance, service_name)router: APIRouter, service_instance: Any, service_name: strNoneExtract ws= entries from http_exposed and register WebSocket endpoints on the router.

register_services() also calls _init_rpc() internally, which registers all rpc_exposed services with the RPCRegistry. You do not need to call _init_rpc() yourself.


Acquire

services.__base.acquire.Acquire

The Acquire class is the dependency injection container. A single instance is created by the Manager and passed to every service whose __init__ accepts an acquire parameter.

Constructor

Takes no parameters. All resources are initialized internally.

Available Resources

ResourceTypeDescription
db_sessionasync_sessionmakerAsync database session factory (SQLAlchemy async_sessionmaker)
schemasdictAuto-discovered schema classes (loaded from schema.py files)
servicesdictAuto-discovered service classes (loaded from service.py files)
settingsSettingsPydantic settings instance (loaded from .env)
utilsmoduleThe utils module (auth helpers, module loader, etc.)
loggerloguru.LoggerPre-configured Loguru logger instance
cacheCacheIn-memory cache instance
deps_cachedeps_cacheDependency-level cache instance
ws_managerWebSocketManagerWebSocket connection manager
tasksTaskRunnerBackground task runner (auto-discovers tasks on init)

Services that do not need dependency injection can omit acquire from their __init__ signature entirely. The Manager will detect this and instantiate the service without it.


ServiceDiscovery

services.__base.discovery.ServiceDiscovery

Utility for discovering and inspecting services by scanning the services/ directory. Used by the CLI, not at runtime.

Constructor

Takes no parameters. Automatically resolves services_dir from its own file location.

Methods

MethodParametersReturn TypeDescription
discover_all(include_core)include_core: bool = Falselist[ServiceInfo]Scan the services directory recursively and return metadata for all discovered services. Core services (e.g. ws) are excluded unless include_core=True.
get_service_info(name)name: strOptional[ServiceInfo]Get info for a specific service by name or path (e.g. "payments" or "v1/payments"). Includes core services in lookup.
build_dependency_graph(include_core)include_core: bool = FalsedictBuild a dependency graph with keys: nodes, edges (RPC), remote_edges (Microservice), blocked (blacklist entries).
get_disabled_services()(none)set[str]Read the set of disabled service paths from .services_disabled.
disable_service(name)name: strNoneAdd a service to the disabled list.
enable_service(name)name: strNoneRemove a service from the disabled list.
is_disabled(name)name: strboolCheck if a service is currently disabled.
service_exists(name)name: strboolCheck if a service directory with service.py exists on disk.

Dependency Graph Return Structure

The build_dependency_graph() method returns a dictionary with the following shape:

{
    "nodes": [
        {"name": "payments", "path": "v1/payments", "rpc_exposed": True, "is_webhook": False}
    ],
    "edges": [
        {"from": "orders", "to": "payments", "type": "rpc"}
    ],
    "remote_edges": [
        {"from": "orders", "to": "billing", "host_url": "http://...", "service_name": "billing", "type": "remote"}
    ],
    "blocked": [
        {"from": "payments", "blocked": "public", "type": "blacklist"}
    ]
}

ServiceInfo

services.__base.discovery.ServiceInfo

A dataclass holding metadata about a discovered service. Returned by ServiceDiscovery.discover_all() and ServiceDiscovery.get_service_info().

Fields

FieldTypeDefaultDescription
namestr(required)The service name (last path segment, e.g. "payments")
pathPath(required)Absolute filesystem path to service.py
path_segmentslist[str](required)Directory segments (e.g. ["v1", "payments"])
module_pathstr(required)Python import path (e.g. "services.v1.payments.service")
api_endpointstr(required)API URL (e.g. "/api/v1/payments")
class_namestr(required)The service class name (e.g. "PaymentsService")
methodslist[str][]HTTP methods (e.g. ["GET", "POST"])
http_exposedlist[str][]Custom route declarations (e.g. ["get=status", "ws=events"])
rpc_exposedboolFalseWhether the service is exposed for RPC
blacklistlist[str][]Services blocked from calling this service via RPC
docstringOptional[str]NoneFirst line of the service class docstring
is_disabledboolFalseWhether the service is in the disabled list
is_coreboolFalseWhether the service is a framework core service
is_webhookboolFalseWhether the service inherits from BaseWebhook or defines events
webhook_eventslist[str][]Event type keys from the events dict
rpc_dependencieslist[str][]Services this service depends on via RPC descriptors
remote_dependencieslist[dict[str, Any]][]Remote microservice dependencies (attr, host_url, service_name)

ServiceGenerator

services.__base.generator.ServiceGenerator

Generator for creating new services with composable templates and mixins. Used by the services create CLI command.

Constructor

Takes no parameters. Resolves services_dir from its own file location.

Methods

MethodParametersReturn TypeDescription
validate_service_name(name)name: strtuple[bool, Optional[str]]Validate a service name. Returns (True, None) on success or (False, error_message) on failure. Checks snake_case, reserved names, Python keywords, and existence.
generate_class_name(service_name)service_name: strstrGenerate a PascalCase class name from the last path segment (e.g. "v1/user_profiles" becomes "UserProfilesService").
generate_template(...)See table belowstrGenerate the full Python source code for a service file.
create_service(...)See table belowPathCreate the service directory, write service.py, and return the file path.
delete_service(name, hard)name: str, hard: boolPathDelete (hard) or soft-disable a service. Returns the service directory path.

generate_template() / create_service() Parameters

ParameterTypeDefaultDescription
namestr(required)Service path (e.g. "v1/users")
descriptionstr(required)Service description for the docstring
basestr"crud"Base template: "crud", "minimal", "empty", or "webhook"
methodsOptional[list[str]]NoneOverride template methods (e.g. ["get", "post"]). None uses template defaults.
rpcboolFalseAdd rpc_exposed = True class attribute
websocketboolFalseAdd WebSocket route (ws=connect) and ws_connect method
authboolFalseAdd JWT authentication (replaces get with authenticated version)
dbboolFalseAdd database session imports
overwriteboolFalseAllow overwriting existing service (create_service only)

Base Templates

crudminimalemptywebhook
Methodsget, post, put, deleteget(none)(none)
AcquireYesNoNoYes
Use caseFull CRUD serviceRead-only endpointCustom service shellWebhook receiver

BaseWebhook

services.__base.webhook.BaseWebhook

Base class for webhook receiver services. Handles raw body extraction, signature verification, event type extraction, and dispatch to user-defined handler methods.

Constructor

ParameterTypeDefaultDescription
acquireAcquire(required)The dependency injection container

Class Attributes

AttributeTypeDefaultDescription
rpc_exposedboolFalseWhether this webhook is exposed via RPC (typically left False)
eventsdict[str, str]{}Mapping of event type strings to handler method names

Methods

MethodParametersReturn TypeDescription
post(request)request: RequestJSONResponseAsync. Webhook entry point registered by the Manager as a POST endpoint. Executes: raw body extraction, verify(), JSON parse, get_event_type(), handler dispatch.
verify(headers, raw_body)headers: dict, raw_body: bytesboolVerify the webhook signature. Override for platform-specific verification (HMAC-SHA256 for Stripe, SHA-1 for GitHub, etc.). Returns True by default.
get_event_type(headers, payload)headers: dict, payload: dictstrExtract the event type string from the webhook. Override for platform-specific extraction. Returns payload.get("type", "unknown") by default.
on_unhandled_event(event_type, payload, headers)event_type: str, payload: dict, headers: dictJSONResponseAsync. Called when an event type has no mapping in self.events. Default: log and return 200 OK with {"status": "ignored"}.

Response Status Codes

ScenarioStatus CodeBody
verify() returns False401{"error": "Verification failed"}
Invalid JSON body400{"error": "Invalid JSON payload"}
Unhandled event type200{"status": "ignored", "event": "<type>"}
Handler method missing from class500{"error": "Handler misconfigured"}
Handler raises exception500{"error": "Internal handler error"}
Handler succeeds200Handler return value

BaseWebhook does not provide built-in idempotency. You are responsible for deduplicating events in your handler methods (e.g. by tracking processed event IDs in your database).


WebSocketManager

common.websocket.WebSocketManager

WebSocket connection manager with organization-level RBAC integration. Manages connection lifecycle and message broadcasting.

Constructor

Takes no parameters. Initializes empty connection and organization tracking dictionaries.

Internal Types

WebSocketConnection (dataclass)

FieldTypeDescription
websocketWebSocketThe FastAPI WebSocket instance
user_idUUIDUser identifier
org_idUUIDOrganization identifier
channel_idstrComputed as "{user_id}_{org_id}"
permissionslistPermission strings for RBAC filtering

WebSocketMessageType (Enum)

MemberValueDescription
CONNECT"connect"Connection confirmation
SUBSCRIBE"subscribe"Channel subscription
UNSUBSCRIBE"unsubscribe"Channel unsubscription
ERROR"error"Error notification
MESSAGE"message"Data message

Methods

MethodParametersReturn TypeDescription
connect(websocket, org_id, user_id, permissions)websocket: WebSocket, org_id: UUID, user_id: UUID, permissions: listNoneAsync. Accept the WebSocket connection, store it, send connection confirmation, and enter the receive loop. Automatically calls disconnect on WebSocketDisconnect.
disconnect(channel_id)channel_id: strNoneAsync. Remove a connection from tracking and clean up organization mappings.
broadcast(org_id, data, resource, required_action, exclude_channel)org_id: UUID, data: dict, resource: str, required_action: str, exclude_channel: Optional[str]NoneAsync. Broadcast a message to all eligible clients in an organization. Only sends to connections whose permissions include "{resource}_{required_action}".