awc-control reference¶
awc-control is the control-plane API server (spec §8.1). It is a stateless
service that runs behind a load balancer. It owns the external REST API and the
trusted internal gRPC API. It MUST NOT proxy ordinary file reads or writes
(spec §8.1).
The binary serves two listeners concurrently until shutdown:
- the
awc-api::v1REST surface over HTTP (axum); - the internal
awc-api::controlgRPC surface over tonic.
Listeners¶
| Surface | Env var | Default | Transport |
|---|---|---|---|
| REST | AWC_CONTROL_LISTEN |
127.0.0.1:8080 |
HTTP (plaintext) |
| gRPC | AWC_CONTROL_GRPC_LISTEN |
127.0.0.1:8081 |
mTLS outside dev; plaintext in dev |
Both listeners bind before the server accepts traffic. The process blocks on a
single ctrl-c or SIGTERM; both surfaces drain and exit together.
Startup sequence¶
- Read
DATABASE_URL. Exit with an error if it is unset. - Connect the Postgres pool (see Database pool).
- Apply pending migrations (see Migrations).
- Load the project membership graph from the
project_membershipstable. - Load active token revocations from the
token_revocationstable. - Build the object store from
AWC_CAS_URL. - Bind the REST and gRPC listeners and serve both until shutdown.
Environment variables¶
| Variable | Default | Meaning |
|---|---|---|
DATABASE_URL |
(none) | Postgres connection string. Required. |
AWC_CONTROL_LISTEN |
127.0.0.1:8080 |
REST listen address. |
AWC_CONTROL_GRPC_LISTEN |
127.0.0.1:8081 |
gRPC listen address. |
AWC_DEV_MODE |
unset | Set to 1 for local dev. Disables gRPC mTLS and relaxes the secret requirements below. |
AWC_SERVICE_TOKEN_KEY |
(none) | HMAC key for service-account tokens. Required when AWC_DEV_MODE is not 1. |
AWC_MOUNT_TICKET_SEED |
(none) | 64 hex chars (32 bytes). Signs mount tickets (spec §9.11). Required when AWC_DEV_MODE is not 1. |
AWC_MOUNT_TARGET_ROOTS |
/opt/sandboxes |
Comma-separated allow-list of mount target roots (spec §13.9). |
AWC_CAS_URL |
memory:// |
Object-store transport. One of memory:// or s3://. |
AWC_CONTROL_TLS_CERT |
(none) | Server certificate path for gRPC mTLS. Required outside dev. |
AWC_CONTROL_TLS_KEY |
(none) | Server private key path for gRPC mTLS. Required outside dev. |
AWC_CONTROL_TLS_CLIENT_CA |
(none) | Trusted client CA path for gRPC mTLS. Required outside dev. |
s3:// for AWC_CAS_URL selects the durable object store, but the s3 cargo
feature is off in the default build (default = []). Rebuild with
--features s3 to enable it; without the feature, an s3:// URL is rejected
at startup.
Local-dev bootstrap¶
These variables only take effect when AWC_DEV_BOOTSTRAP=1. They never apply
to a production deployment.
| Variable | Default | Meaning |
|---|---|---|
AWC_DEV_BOOTSTRAP |
unset | Set to 1 to grant the dev principal the Admin role and mint a dev service token. |
AWC_DEV_TENANT |
ten_01arz3ndektsv4rrffq69g5fav |
Tenant for the bootstrap grant. |
AWC_DEV_PROJECT |
prj_01arz3ndektsv4rrffq69g5fav |
Project for the bootstrap grant. |
AWC_DEV_PRINCIPAL |
prin_01arz3ndektsv4rrffq69g5fav |
Principal that receives Admin. |
When AWC_SERVICE_TOKEN_KEY is also set, the bootstrap logs a full-scope
service token for the dev principal. The token is a dev-only credential with a
fixed 24-hour lifetime.
Database pool¶
The database layer builds a single Postgres connection pool and shares it across every repository. Each repository clones the pool; the pool is reference-counted.
| Setting | Value | Override |
|---|---|---|
max_connections |
16 |
None (fixed in code). |
min_connections |
sqlx default | None. |
acquire_timeout |
sqlx default | None. |
The pool size is not exposed as an environment variable. It is fixed in code and requires a rebuild to change.
Migrations¶
Migrations are forward-only. The SQL migration files are embedded at compile time. There are no down or rollback files. The server applies pending migrations on startup before it accepts requests.
awc-control runs repos.migrate() after it connects. awc-worker also runs
repos.migrate() on its own startup, so either binary can bring a fresh
database up to the current schema.
The embedded set, in order:
| Version file | Adds |
|---|---|
20260807000001_init.sql |
Core schema (see table below). |
20260807000002_job_dedupe.sql |
jobs.dedupe_key. |
20260807000003_gc_runs.sql |
gc_runs, gc_deletion_plans. |
20260807000004_gc_integrity_holds.sql |
gc_integrity_holds. |
20260808000005_project_memberships.sql |
project_memberships. |
20260808000006_token_revocations.sql |
token_revocations. |
The init migration creates these tables: tenants, projects, principals,
hosts, snapshots, workspaces, workspace_leases, mount_sessions,
mount_tickets, import_sessions, jobs, idempotency_keys, audit_events,
retention_policies.
REST surface¶
All /v1/* routes except /v1/health require a bearer token and pass through
the per-principal rate limiter. Every mutation accepts an Idempotency-Key
header (spec §13.1). Every response echoes an x-request-id header.
| Method | Path | Spec | Purpose |
|---|---|---|---|
POST |
/v1/import-sessions |
§13.3 | Open an import session. |
POST |
/v1/import-sessions/:id:batchCheckObjects |
§13.4 | Check which declared objects are missing. |
POST |
/v1/import-sessions/:id:complete |
§13.5 | Complete an import; creates a VALIDATING snapshot. |
GET |
/v1/snapshots |
— | List snapshot metadata. Requires project_id. No dedicated spec section. |
GET |
/v1/snapshots/:id |
— | Get one snapshot's metadata. No dedicated spec section. |
GET |
/v1/snapshots/:id:diff |
§13.14 | Compare two root trees. Only the inline json format is implemented. |
POST |
/v1/workspaces |
§13.6 | Create a workspace from a READY snapshot. |
GET |
/v1/workspaces/:id |
— | Get one workspace's metadata. No dedicated spec section (§13.6 defines the resource shape). |
POST |
/v1/workspaces/:id:fork |
§13.7 | Fork one or more workspaces. |
POST |
/v1/workspaces/:id:issueMountTicket |
§13.8 | Issue a signed mount ticket. |
GET |
/v1/health |
— | Unauthenticated liveness probe. Returns ok. |
Action verbs travel in the path segment after a colon (for example,
/v1/import-sessions/import_01...:complete).
gRPC surface¶
Both services are privileged internal RPCs. They are served with mutual TLS outside dev mode (spec §13.1). Each handler binds the client certificate fingerprint to the registered host that owns the mount session.
| Service | RPC | Spec |
|---|---|---|
LeaseService |
ActivateWorkspaceLease |
— |
LeaseService |
RenewWorkspaceLease |
§13.10 |
LeaseService |
ReleaseWorkspaceLease |
— |
CheckpointService |
PublishCheckpoint |
§13.12 |
Only RenewWorkspaceLease (§13.10) and PublishCheckpoint (§13.12) have
dedicated spec sections. ActivateWorkspaceLease and ReleaseWorkspaceLease
are implemented in the control plane but not separately specified.
RenewWorkspaceLease is clock-independent. The control plane sets the new
expiry from its own policy. The default renewal TTL is 90 seconds. A
renewal returns one of: extend, remount read-only, revoke, or workspace gone.
PublishCheckpoint maps onto the control-plane checkpoint publish path. The
control plane completes every mutable update for a checkpoint in a single
PostgreSQL transaction (spec §13.12).
Authentication¶
The REST authenticator reads an Authorization: Bearer <token> header.
| Credential | Recognized by | Verified with |
|---|---|---|
| Service-account token | awcsat1_ prefix |
AWC_SERVICE_TOKEN_KEY (HMAC). |
| OIDC id token | Any other bearer | The configured OIDC verifier. |
Project RBAC is loaded once at startup from the project_memberships table and
held as an immutable serving snapshot. Restart the process to pick up durable
membership changes.
mTLS requirement¶
Outside dev mode, the gRPC listener requires mutual TLS. The mTLS setup configures a server identity and a required client CA, so a connection without a trusted client certificate never reaches a service handler.
AWC_DEV_MODE=1 disables mTLS. In dev mode the server serves plaintext gRPC
and the handlers accept requests that carry no client certificate.
Failure behavior¶
Database failure (spec §17.6)¶
PostgreSQL MUST be deployed with automated failover and point-in-time recovery in production. The system MUST never reconstruct a mutable workspace head by guessing from object-store contents. Object-store snapshots are immutable candidates; PostgreSQL determines which one is the acknowledged head.
After a database restore, reconciliation compares published snapshot records, workspace versions, audit events, object-store snapshot manifests, and host-reported mount sessions. Unreferenced immutable objects stay safe and are reaped by garbage collection.
Control-plane outage (spec §17.5)¶
Existing immutable reads and local writes do not require the control plane. During an outage:
- a writer lease cannot be renewed authoritatively;
- a checkpoint cannot be published;
- a new mount ticket cannot be issued;
- a workspace cannot be forked or deleted.
awcd MAY continue a writable mount through a bounded lease grace period.
After grace expiration it MUST deny checkpoint publication and SHOULD remount
the workspace read-only or pause the sandbox.
Current scope¶
The following are specified but not yet implemented or exposed in the current build:
- REST TLS. Spec §13.1 requires the external API to be HTTPS. The binary serves REST as plaintext HTTP and expects TLS termination upstream (for example, at a load balancer). There is no in-process REST TLS option.
- Rate limiting. Spec §16.10 describes admission throttling. The server constructs an unlimited limiter. There is no environment variable to set a limit.
- OIDC discovery. The server wires a static OIDC verifier with an empty
trusted set. Real OIDC discovery is a follow-up. Until then, only
service-account tokens authenticate, and only when
AWC_SERVICE_TOKEN_KEYis set. - gRPC request id.
PublishCheckpointdoes not yet surface anx-request-idfrom gRPC metadata into the audit record. - Durable membership seeding. The in-memory membership store loads only
from
project_membershipsat startup. There is no admin API to mutate it at runtime.