Skip to content

Domain ownership

Implemented

Source: doc/architecture/Domain_Ownership_Map.md · packages/services/*

Service packages own their tables, their NATS subjects, and their state machines. No cross-package direct joins. Cross-domain data flows through NATS events or explicit API calls.

Ownership matrix

flowchart LR
    classDef svc  fill:#e8f5e9,stroke:#2e7d32
    classDef tbl  fill:#e3f2fd,stroke:#1565c0
    classDef sub  fill:#fff3e0,stroke:#e65100
    classDef sm   fill:#f3e5f5,stroke:#6a1b9a

    %% auth
    AUTH[auth]:::svc --> AUTH_T1[oidc_sessions]:::tbl
    AUTH --> AUTH_T2[service_accounts]:::tbl
    AUTH --> AUTH_T3[user_posix_identities]:::tbl

    %% inventory
    INV[inventory]:::svc --> INV_T1[sku_catalog]:::tbl
    INV --> INV_T2[nodes]:::tbl
    INV --> INV_T3[node_resource_slots]:::tbl
    INV --> INV_T4[os_images]:::tbl
    INV --> INV_T5[node_image_cache]:::tbl

    %% provisioning
    PROV[provisioning]:::svc --> PROV_T1[allocations]:::tbl
    PROV --> PROV_T2[allocation_resource_claims]:::tbl
    PROV --> PROV_T3[allocation_placements]:::tbl
    PROV --> PROV_T4[node_tasks]:::tbl
    PROV --> PROV_S1[provisioning.* subjects]:::sub
    PROV --> PROV_SM[Allocation FSM]:::sm

    %% billing
    BILL[billing]:::svc --> BILL_T1[usage_records]:::tbl
    BILL --> BILL_T2[ledger_entries — immutable]:::tbl
    BILL --> BILL_T3[low_balance_events]:::tbl
    BILL --> BILL_S1[billing.* subjects]:::sub
    BILL --> BILL_SM[Billing FSM]:::sm

    %% payments
    PAY[payments]:::svc --> PAY_T1[payment_sessions]:::tbl
    PAY --> PAY_T2[payment_webhook_events]:::tbl
    PAY --> PAY_S1[payments.* subjects]:::sub

    %% terminal
    TERM[terminal]:::svc --> TERM_R[Redis: terminal_token:*]
    TERM --> TERM_SM[Session FSM]:::sm

    %% appruntime
    APR[appruntime]:::svc --> APR_T1[app_instances]:::tbl
    APR --> APR_T2[app_instance_members]:::tbl
    APR --> APR_T3[app_instance_events]:::tbl
    APR --> APR_T4[app_manifests]:::tbl

    %% storage
    STG[storage]:::svc --> STG_T1[storage_objects]:::tbl
    STG --> STG_T2[storage_namespaces]:::tbl

    %% admin
    ADM[admin]:::svc --> ADM_T1[audit_logs — immutable]:::tbl
    ADM --> ADM_T2[refund_records]:::tbl

    %% releases
    REL[releases]:::svc --> REL_T1[allocation_ssh_public_keys]:::tbl

    %% notification
    NOTIF[notification]:::svc --> NOTIF_R[Redis pub/sub]

Service summary table

Service Owns tables Emits subjects State machines
auth oidc_sessions, service_accounts, user_posix_identities Session lifecycle
inventory sku_catalog, nodes, node_resource_slots, os_images, node_image_cache Node lifecycle, slot lifecycle
provisioning allocations, allocation_resource_claims, allocation_placements, node_tasks provisioning.requested, .active, .failed, .releasing.requested, .releasing.completed, .release_failed, .force_release_requested Allocation FSM
billing usage_records, ledger_entries, low_balance_events billing.low_balance_warning, .auto_release_pending, .balance_depleted Billing FSM
payments payment_sessions, payment_webhook_events, ledger credit rows payments.balance_credited
terminal (Redis) Session FSM
appruntime app_instances, app_instance_members, app_instance_events, app_manifests (app-runtime subjects) App instance FSM
storage storage_objects, storage_namespaces
admin audit_logs, refund_records
releases allocation_ssh_public_keys
notification (Redis pub/sub channels)
maas (facade) Bare-metal node FSM (proxies MAAS)

Cross-domain data flow

Cross-domain queries are forbidden. To get data across domains:

flowchart LR
    A[Service A] -->|HTTP call<br/>via package import| B[Service B's API]
    A -->|subscribes to| NATS[(NATS subject)]
    NATS -->|emitted by| B
    A -->|sees consistent view in| RM[(Read-model cache<br/>packages/shared/readcache)]

Three legitimate paths:

  1. Direct Go call within cmd/api (services are imported as packages).
  2. NATS subscription (workers; never publish directly from handlers).
  3. Read-model cache (packages/shared/readcache) for cross-domain UI reads — see UI_Read_Model_Cache_Architecture_v1.md.

Authority for shared concepts

Concept Authority Reason
Tenant / project hierarchy auth + authz Identity is the root; everyone else consults
Allocation existence + state provisioning All other domains derive from it
Money billing + payments Immutable ledger; credits and debits never UPDATE
Audit admin Centralized; every privileged mutation writes here
Node lifecycle inventory Including slot map approval
Policy values policy shared package + policy_values table Scoped resolution; no hardcoded constants

What package boundaries do not mean

  • They do not imply network boundaries today. cmd/api imports every service package directly. Phase-2 extraction is planned but not done; Inter_Service_Communication.md documents the eventual split.
  • They do not imply separate databases. One Postgres instance; per-service credentials enforce boundaries at the user/grant level.

Where to look next