Configure & Attest
How per-deployment configuration and per-app attestation extensions work on the Privasys Developer Platform.
Privasys apps configure themselves on first boot and bind opaque values into their RA-TLS leaf certificate at runtime. Two declarations drive this:
| Declaration | Where | Purpose |
|---|---|---|
config_api | org.privasys.config_api OCI label (containers), or privasys.json (containers fallback), or the @config-api WIT decoration (wasm) | One-shot in-process endpoint the deployer POSTs to before the app is allowed to serve other traffic. For wasm, naming a function with @config-api automatically marks it as the freeze-gate AND restricts it to the app's owner team — no separate @auth line is required. |
| Per-app OID extensions | Set by the app at runtime via the SDK (setAttestationExtension) | Per-app X.509 extensions under 1.3.6.1.4.1.65230.3.5.*, baked into the RA-TLS leaf and replayed across restarts. |
Per-app owners team
The deployer of an app is automatically added to its owners team (a
list of platform OIDC sub claims persisted on the management service
under app_owners). Owners can:
- Deploy / re-deploy the app.
- Call
@config-api(ororg.privasys.config_api) endpoints — i.e. the freeze-gate. Even a platformmanagercannot configure an app they don't own; the configure entrypoint is private to the team. - Manage the team itself via:
GET /api/v1/apps/{id}/ownersPOST /api/v1/apps/{id}/owners(body:{sub, email, name})DELETE /api/v1/apps/{id}/owners/{sub}
The original creator (apps.owner_sub) cannot be removed from the team
— this guarantees there is always at least one principal able to
recover the app. The owners list is shipped to the enclave on
wasm_load/container_load and persisted with the app metadata, so
owner-only calls keep working across enclave restarts without
consulting the platform.
Lifecycle
- Deploy. A user (or a token bearing
privasys-platform:manager) deploys an app version to an enclave. The deploy call carries onlyenclave_idand the version pin — no per-app secrets. - Wait for app. The container or wasm module starts and binds its listener.
- Freeze. The manager intercepts the first request to the app's
hostname and replies
503 Retry-Afterto anything that does not matchconfig_api.{method,path}. The configure path is forwarded in-process. - Configure. The deployer POSTs the configuration payload to
${config_api.path}. The app validates it, seals it to its own storage (encrypted volume / sealed KV), and callssetConfigComplete(). The manager flips the freeze flag. - Serve. All subsequent traffic is forwarded normally.
- Restart. On any restart, the app reloads its sealed config from storage; the manager keeps the freeze flag set so the configure step never runs twice for the same payload.
Per-app attestation extensions (OID 1.3.6.1.4.1.65230.3.5.*)
Apps that want to bind opaque values into their RA-TLS leaf certificate — for example: an MCP server URL list, a vector-DB project ID, a hash of a configured API key — call the SDK at runtime:
setAttestationExtension(arc_suffix: u32, value: list<u8>)The platform:
- Asserts the OID is under
1.3.6.1.4.1.65230.3.5.<arc_suffix>and under the calling app's namespace. - Stores the
(oid, value)pair durably so it survives restarts. - Re-issues the per-app RA-TLS certificate so subsequent connections see the new extension immediately.
- On restart, replays the stored extensions into the leaf cert before unblocking traffic.
This means the OID set is part of the verified app surface and is owned by the app itself, not by the operator at deploy time.
SDK surface
Both wasm and container apps expose the same two SDK calls:
| Call | Effect |
|---|---|
setAttestationExtension(arc_suffix, value) | Install or replace the per-app OID extension. Updates the RA-TLS cert. |
setConfigComplete() | Mark this app as configured. Lifts the manager's freeze gate. Idempotent. |
The intended pattern in the configure handler is:
- Validate the inbound payload.
- Persist anything that needs to survive restart (sealed KV / encrypted volume).
setAttestationExtension(<your-arc>, sha256(payload))so verifying clients can confirm the running app saw exactly the configuration they delivered.setConfigComplete().- Return 2xx.
Verifying as a client
A client connecting to the app:
- Performs the standard RA-TLS handshake; the leaf cert carries the TDX/SGX quote.
- Pulls the per-app OIDs out of the leaf and recomputes the expected value (e.g. SHA-256 of the API key it provisioned).
- Equality means this enclave instance was configured with exactly that input — no env var indirection, no out-of-band attestation document needed.