# aidion: the operation engine **Richard Heycock** aidion keeps a running application true to its package. It installs only artefacts that match the digests their manifest declares. It carries authority with every request as a capability token that any holder can narrow. It records each consequential action in an audit chain whose links a separate key server computes, and it resumes interrupted work from the last completed step. ## 1 What aidion is A **tenant** is one organisation served from a shared installation. A **package** is a versioned archive of artefacts, with a manifest listing the services it provides and the packages it depends on. A **binding** records the package version bound to a logical service name. aidion consists of these parts: | Part | Language | Role | |---|---|---| | aidion | Elixir (Phoenix, Ash), Postgres | API, packages, bindings, monitoring, authority | | aidion-registry | Go, Restate SDK | durable workflow services | | key-server | Elixir | tenant key custody and cryptographic operations | | audit | Elixir | audit outbox, forwarding and the hash-chained store | | macaroons | Elixir | capability tokens, and their enforcement in Ash and Phoenix | | restate_ex | Elixir | SDK for writing Restate services | | restate_client_ex | Elixir | client for invoking Restate services | | nomad_ex | Elixir | client for the Nomad HTTP API | | aidion-cli | Elixir | operator command line | It relies on four external systems: Nomad places and runs workloads, Restate executes workflows durably, Postgres holds state and an S3-compatible store holds artefacts. ## 2 Packages ### Upload A package arrives as an archive holding a manifest and its artefacts. aidion decodes the manifest and checks each artefact's SHA-256 digest and byte size against it. aidion validates a job template in the manifest with Nomad's parser. It stores artefacts under their content address, `sha256:`, and the archive under the package's identifier. A package is unique by name, version and tenant. ### Dependencies The manifest pins each dependency by name, version and digest, resolved within the uploader's tenant. aidion records a dependency that has yet to arrive as waiting, and links it when it arrives. aidion computes the dependency closure of a package and reports what is missing from it. ### Install Install checks the dependency closure first. A package with an incomplete closure installs only when the caller's token permits an incomplete install. aidion records such an install as incomplete. aidion then renders a job for every service in the package before submitting any. A package starts only when every one of its services renders. Each job fetches its artefact from aidion by digest, and Nomad verifies the download against that digest. ### Retirement aidion retires a package only once the bindings that refer to it are deleted. ## 3 Services and bindings A binding records a logical service name, the version and artefact digest bound to it, the endpoint the service listens on and the binding's tier: `running`, `stopped` or `archived`. When aidion sets a binding, aidion finds the binding's package by artefact digest within the caller's tenant. aidion deletes a binding only once it is archived. aidion applies a tier change to the scheduler first, and records the new tier once the scheduler accepts it. `running` scales the service's job up, and resubmits the package's jobs if the job is absent. `stopped` scales the job to zero. `archived` stops it. ### The sync agent The **sync agent** follows the scheduler's allocation event stream. When an allocation starts running, the agent writes the allocation's address to the binding as the endpoint and registers the deployment with Restate. When an allocation completes, fails or is lost, the agent clears the endpoint. The agent stores its position in the event stream. On restart it reconciles every current allocation, then resumes the stream from the stored position. In production the sync agent refuses an endpoint at a private address. ## 4 Monitoring aidion keeps the current state of every bound service, its allocations and the nodes they run on. It builds that state from the scheduler's allocation, deployment, job, evaluation and node events. From these it derives one status per service, by fixed precedence: `failing`, `pending`, `degraded`, `unknown` or `healthy`. aidion records each scheduling failure with the scheduler's reason. A health agent polls each running service's health endpoint every ten seconds and records the result as passing, warning or critical. ## 5 Durable workflows Workflows run on Restate, which journals each step. A workflow interrupted by a failure resumes from its last completed step. The **identity layer** is a Restate virtual object for each logical name. When aidion sets a binding, it records the binding's version and artefact digest there. The **invocation workflow** resolves a logical name through the identity layer and invokes the service at the version it resolved. Restate journals the resolution, so a replayed invocation uses the same version even if the binding changes in the meantime. A **schedule** invokes a service once after a delay, or repeatedly on a cron expression. Each wait is a durable timer, so it survives a restart. ## 6 Authority ### Sign-in A password is used only to sign in. The API returns a macaroon in exchange. The macaroon is the only credential the API issues. ### Macaroons A **macaroon** is a bearer token carrying caveats, each a condition on what its holder may do. aidion mints each token with four caveats: the subject, the tenant, the actions permitted and an expiry, set by default to eight hours after minting. aidion derives the permitted actions from the user's role (`admin`, `operator` or `viewer`) as capabilities for each domain. The token's signature is an HMAC-SHA256 chain. The first link is an HMAC under the tenant's root key. Each caveat adds a link. Adding a caveat needs only the token, so any holder can narrow it. Removing a caveat breaks the chain. Verification recomputes the chain, compares signatures in constant time and accepts only caveats of the types it recognises. ### Enforcement Every API route except sign-in, sign-out and health requires a macaroon. aidion checks the macaroon's signature when the request arrives. An authoriser then checks the macaroon's caveats against the resource action the request performs, and refuses an expired token. aidion's workflow services verify the token on each request they receive. aidion's background agents act under a service token limited to the registry, monitoring and reading packages, minted again before it expires. ## 7 Keys The **key server** holds each tenant's keys and performs cryptographic operations with them for its callers. Key material stays inside it. Each key belongs to a tenant and a purpose: HMAC-SHA256, Ed25519 signing or X25519 key agreement. ### Envelope encryption The key server derives a key-encryption key from a site master key with HKDF-SHA256 each time it needs one. Each tenant has a data key of its own, encrypted under the key-encryption key. Each of the tenant's keys is encrypted under the tenant's data key. Every layer uses AES-256-GCM with the tenant, purpose and version bound into the authenticated data, so a ciphertext decrypts only for the tenant and purpose it was made for. The key server holds decrypted keys only in memory. The database stores keys only in encrypted form. ### Lifecycle Each tenant and purpose has a versioned chain of keys, with one version active. Rotation creates a new active version. A rotated key keeps verifying and stops signing, so records made under it remain checkable. Revocation withdraws a key from every operation. ### Callers In production the key server identifies each caller by its mutual-TLS client certificate. An explicit list states which caller may perform which operation for which purpose. Only administrators can revoke a key. ## 8 Audit ### The chain Each tenant's audit events form a hash chain. An event records the actor, the resource, the event type, its context and the state before and after. Its hash is an HMAC-SHA256 over a canonical encoding of the event, its sequence number and the previous event's hash. The first event chains from a genesis value for the tenant. The key server computes each HMAC and is the only holder of the chaining key. The audit service assigns sequence numbers under a lock for each tenant, which gives the chain a single order. ### Verification The key server verifies each link in constant time, so it detects a changed or missing event at the first link after it. At intervals the audit service gathers a period's event hashes into a Merkle tree. The key server signs the tree's root with the tenant's Ed25519 key, and the audit service keeps the root in write-once storage, so a third party can check the trail with the tenant's public key alone. ### The store The audit service stores events in Postgres, keyed by tenant and sequence number. The database grants the application role insert and read rights only, and revokes update and delete from every role. The audit service recognises a repeated event by its identifier and leaves the stored row as it is. ### Delivery The audit client writes each event first to a durable outbox on the machine that produced it, under a key that prevents a retry from recording it twice. A forwarder delivers it through Restate and retries with backoff. The forwarder sets aside an event whose delivery fails, for an operator to requeue. When the outbox is near its limit it accepts only events marked critical. ### What aidion records Sign-ins, package uploads, installs and retirements, binding changes, tier transitions, invocations, schedules and artefact fetches. ## 9 Interfaces ### HTTP API JSON over HTTP, authorised by macaroon. | Method | Path | Action | |---|---|---| | POST | `/api/auth/sign_in`, `/api/auth/token` | exchange a password for a macaroon | | GET | `/api/health` | health | | GET, POST | `/api/packages` | list, upload | | GET, DELETE | `/api/packages/:uuid` | show, retire | | GET | `/api/packages/:uuid/manifest` | the signed manifest | | POST | `/api/packages/:uuid/install` | install | | GET | `/api/bindings` | list bindings | | GET, POST, DELETE | `/api/bindings/:name` | show, create or update, delete | | POST | `/api/services/:name/tier` | change tier | | POST | `/api/services/:name/invoke` | start the invocation workflow | | GET | `/api/services/:name/status` | service status | | GET | `/api/monitoring/overview`, `/api/monitoring/nodes`, `/api/monitoring/nodes/:id/allocations` | current state | | POST | `/api/workflows/schedule` | schedule a one-off invocation | A separate node listener serves artefacts by digest to the scheduler's clients. A metrics listener serves Prometheus metrics. ### Command line The `aidion` command covers every API route. It also builds and inspects package archives offline. ## 10 Deployment aidion needs Postgres, Nomad, Restate, an S3-compatible store, the key server and the audit service. In production aidion refuses to start in any of these cases: Restate, Nomad or the artefact store is reached over plain HTTP or at a private address; its token-signing secret is unset. A production build that allows private endpoints fails to compile. Services authenticate to one another with mutual TLS, using short-lived certificates issued by an internal certificate authority. ## 11 Libraries **restate_ex** is an SDK for writing Restate services in Elixir. It supports services, virtual objects and workflows, with journalled side effects, durable timers, state, awakeables and workflow promises. It speaks Restate's service protocol versions 6 and 7, in request-response or bidirectional streaming mode. It verifies Restate's signed requests and includes a client for Restate's admin API. **restate_client_ex** invokes Restate services through the ingress: calls, one-way sends with an optional delay, workflow submission and attachment, and awakeable completion. It retries requests that carry an idempotency key. **nomad_ex** is a client for the Nomad HTTP API. It covers jobs, allocations, nodes, deployments, evaluations and ACL tokens, over TLS, with telemetry on every call. **macaroons** implements the capability tokens: minting, attenuation, verification and extraction of capabilities. Its typed caveats cover tenant, subject, resource, domain, action, expiry, path, signer and artefact digest. It includes an authoriser for Ash resources and a Phoenix plug. **audit** consists of three applications: a core library holding the event contracts and canonical encoding, a client holding the outbox and forwarder and a server holding the chain and the store.