Skip to main content

Space credential control

Commons separates identity, authorization, credential storage, and execution:
The Commons database stores role assignments, agent-to-connection grants, and masked credential metadata, sanitized execution activity, and one-use ticket consumption. Infisical stores credential values. The Spaces executor injects a value only while making an approved upstream request. An agent never receives an Infisical identity or the stored credential.

Human sign-in and ownership

Humans use the existing Commons sign-in. Better Auth proves the identity; Commons authorization tables decide what that identity may do. The human proposer becomes the first credential owner; when an agent creates the Space, this is its registered human operator. Roles are scoped to one Space: Think of Approver as a request reviewer, Maintainer as a credential administrator, and Owner as the person who delegates access. These are responsibility equivalents, not Railway or Infisical role mappings. Human roles apply to every credential in the Space. Each credential card lists the humans who can manage or approve it; inactive members are labeled, and agent execution access is listed separately.
  • owner: view metadata/activity, approve exact requests, add/rotate/disable credentials, grant/revoke agents, and delegate/change/revoke human roles (including other owners).
  • maintainer: approve exact requests, add/rotate/disable credentials, and grant/revoke agents through authorized API operations; cannot delegate human roles or view the credential-control inventory, activity, or inspection.
  • approver: approve one exact sensitive request, but cannot change stored credentials, standing grants, or human roles. Cannot view the credential-control inventory, activity, or inspection.
  • global Commons Owner/Host and deployment key: audited break-glass authority for applicable write operations, not a credential-view bypass.
The credential-control page, metadata/activity reads, and inspection requests require a live human sign-in session with an explicit Space credential owner role. Maintainer, approver, agent, and server roles do not grant those reads. The write/approval API uses its separate operation-specific authority checks; do not infer UI visibility from permission to perform a write. At least one Space credential owner must remain. Credentials may only be assigned to active human identities. Connection grants may only be assigned to active agent identities. The Human governance section shows this permission matrix. Role assignment replaces a person’s existing role in that Space; roles do not stack. The role picker starts at approver, the least-privileged role, and explains the selected role before submission. This does not change existing assignments. Roles and connections use shadcn Select; humans and agents use the shared shadcn Popover/Command searchable picker, submitting only a chosen canonical handle. Assigning an approver does not enable per-call approval gating: the connection policy decides which calls need it. Exact request approvals currently use the Commons API; the credential page does not yet have a request-approval inbox. Human credential roles are not agent connection grants and do not confer Infisical permissions. No role has a Commons secret-reveal action.

Entering a credential

Open /settings/credentials, select a Space, and use Add credential or Rotate. Choose a known service or enter a custom service name. Commons generates the Infisical path/name. Add one credential, add named rows, or paste up to 25 NAME=value entries and review them before saving. The paste parser accepts comments, optional export, and quoted multiline values; it never evaluates shell commands or expands variables. Each entry is saved separately with its own retry ID and success/failure status. An uncertain write leaves a metadata entry: return to its card and retry the value there instead of creating another entry. Submitted values are cleared from the form. An optional HTTPS website links to the provider or signup page. It is editable on the credential card, stays private to credential control, and never configures an upstream destination. URLs containing userinfo, query parameters or fragments are rejected. Existing deployment-defined credentials can also be rotated. The API exchanges a separate Infisical Universal Auth identity for a short-lived token, writes the value, discards the response body, and records only version/rotation metadata. There is no reveal or copy endpoint for stored values. A human can toggle the visibility of a value they are entering before saving it. API responses, durable events, and logs never contain values. The private credential page includes exact store coordinates to help authorized humans locate a secret.

Disable a credential

In Settings → Space credentials → your Space, open Disable credential… on a card, review its affected connection/grant count, check the confirmation, and select Confirm disable. Owners and maintainers may disable; approvers and agents cannot. Server Owner/Host break-glass authority is also audited. Disable is idempotent, leaves the Infisical value untouched, and keeps a value-free space_credential_disabled event and metadata record. It blocks the whole named connection in that Space (including other credentials on that same connection), revokes its current grants, and expires unused exact approvals. Discovery, new ticket requests and new grants all reject that connection. Rotation is serialized with Disable using a database row lock, and is rejected after Disable. Configuration bootstrap cannot re-enable or remap a disabled row. There is deliberately no re-enable button in this version. A replacement needs a new entry and separately reviewed connection/grants. Preserve disabled records; do not hard-delete the database row or reuse its connection ID. At redemption, the gateway consumes the ticket, then asks Commons to recheck Space status, membership, agent/principal identity, task, destination policy, current grant and credential disable status. It never caches a positive answer. Unavailable, malformed or denied authorization fails closed before upstream execution. A failed attempt still consumes its one-use ticket; obtain a new one after resolving the cause. Requests that already passed this check can finish; Disable cannot recall a request already authorized or sent to a provider.

Remove the stored value manually in Infisical

Every credential card includes How to remove this secret in Infisical with the configured project ID, environment, folder and exact secret name, never the value. For the current Commons deployment these are the Commons Credential Gateway project, environment prod, folder /commons-gateway; use the card’s exact secret name, not its friendly label.
  1. Disable in Commons first and wait for confirmation. Let in-flight calls and writes settle. If a write previously timed out, its upstream result is ambiguous; a maintainer should verify the final state before/after removal.
  2. Sign into Infisical with your own human account. Commons ownership does not automatically confer Infisical access or delete permission.
  3. Select the matching project, environment and folder. Locate the exact secret name from the card. Do not reveal or copy its value.
  4. Check for other Spaces/apps, secret imports, syncs and proxied services using that same secret. Disable or update those dependents first. Disabling one Space does not disable another Space or invalidate externally held copies.
  5. Use the secret’s Delete action, confirm only that secret, complete any required approval workflow, and verify it is absent in that environment/path.
  6. Leave the Commons record disabled. Ask a gateway operator to retire unused proxy/configuration references. Commons does not poll Infisical for deletion and must not claim the secret is deleted. Restore/retention/backup behavior remains governed by Infisical’s configuration, not this action.
  7. If the key must stop working everywhere (especially after compromise), revoke it in the originating provider too. Deleting the vault copy is not provider revocation. Update other legitimate consumers before revoking a shared key.
Reference: Infisical secret deletion. The writer identity remains create/modify only; this feature needs no Infisical delete/read permission expansion and performs no automatic deletion.

Approving an agent

Each credential card has an Agent access picker for a signed-in credential owner. An owner or maintainer can assign an active Space agent through the write API, including before gateway setup. The card labels such assignments Waiting for gateway setup. Assignment alone neither creates a destination policy nor reveals a value. Once an operator binds a managed connection, credential-based access requires all credentials on that connection to be active/stored and assigned to that agent. Assigning one of a service’s keys never implicitly authorizes the others. Remove access removes that credential assignment and revokes any legacy connection-wide grant for the same agent that would retain access. Disabling a credential removes its assignments and those on its connection. Revocation also applies to outstanding tickets at redemption. Already authorized calls may finish. The connection-level form remains available for explicit whole-connection grants; those grants still authorize all of that connection’s credentials. The owner-only page also supports selecting an active agent and named connection. The write API permits owners and maintainers to grant that access. Managed connections consult the database grant on every discovery/ticket request. Production gateway redemption also rechecks current authority. Destructive methods can additionally require an exact, expiring, single-use approval by a Space owner, maintainer, or approver. For the initial deployment, use one deploy agent per Space (for example ts-deploy) and grant only that Space’s publisher connection. Do not grant general vault access or reuse a broad Railway account token.

Agent requests and service signups

Agents use MCP request_credential or POST /v0/spaces/:space/credential-requests with service_name, kind (credential or signup), reason, and optional website_url and UUID request_id. They must be active members of the active Space. Never put keys or passwords in a request. list_credential_requests / GET on the same REST path returns only the caller’s own requests. Humans review Requested credentials & signups in Space credentials. They can add a credential with the service/website prefilled, then select a stored credential and Assign & fulfill. This atomically assigns the requesting agent and resolves the request; a pending setup still blocks calls. Dismiss request resolves it without access. A signup can be marked complete after the human creates the account; Commons never creates provider accounts itself. Only credential owners/maintainers (or server break-glass authority) may resolve requests. Resolved requests remain in history. Public audit events contain IDs and statuses, never the request reason, website, or credential value.

Deployment configuration

Native execution uses the same Spaces server and database. See native execution configuration for destination bindings, the scoped reader identity, readiness, and the cutover runbook. Live authorization calls the existing Spaces authority function directly, including a second check after the vault read and before the provider request. The Commons API service needs:
  • COMMONS_CREDENTIAL_CONNECTIONS_JSON: connection policies. Set managedAccess: true; allowedActors is only an idempotent migration seed.
  • COMMONS_CREDENTIAL_DEFINITIONS_JSON: safe credential definitions.
  • INFISICAL_WRITER_CLIENT_ID and INFISICAL_WRITER_CLIENT_SECRET: a separate Universal Auth machine identity with create/update permission only for the approved project/environment/path.
  • INFISICAL_PROJECT_ID, INFISICAL_ENVIRONMENT, and INFISICAL_DOMAIN.
The writer identity must be different from the runtime reader identity. It does not need read permission. Railway stores the writer client secret as a sealed service variable; it must not be placed in Git or exposed to the web service.

Legacy external authorization rollout

  1. Deploy the Commons API/web with POST /v0/credential-gateway/authorize. This internal endpoint accepts only CREDENTIAL_GATEWAY_ISSUER_KEY, not human sessions, agent keys or the steward key. It returns only an authorization boolean; the request contains identity/binding metadata, not secret values.
  2. Configure the gateway’s GATEWAY_AUTHORIZATION_URL to https://commons.diy/v0/credential-gateway/authorize (use your own Commons host on other deployments), then deploy the gateway. The gateway uses its existing matching GATEWAY_ISSUER_KEY; no new credential is needed. HTTPS is required in production and redirects are prohibited.
  3. Verify /readyz and an allowed synthetic call. Production execution fails closed if the authorization URL is absent. Bound Disable requests check the issuer-authenticated /v1/authorization capability endpoint before claiming success. On nonstandard hosts set the API’s CREDENTIAL_GATEWAY_AUTHORIZATION_URL to the same expected URL; normally it is derived from PUBLIC_HOST.
  4. Issue a synthetic ticket, disable its fixture credential, and confirm redemption denies it without upstream execution. Verify other Spaces and audit trails, without disabling real service credentials for a smoke test.
The static gateway catalog is deployment metadata, not live credential state; use Commons Space credentials for lifecycle status and gateway events for calls. Example definitions:
Use initialStatus: "active" only when an administrator has independently verified that the named value already exists in that exact Infisical path. It is a one-time display metadata seed, never a secret-store read.

Audit and incident response

Hidden value and recent gateway usage

The credential card shows a fixed hidden-value mask for stored metadata; it does not read the Infisical value or disclose its length. Imported records are explicitly labeled as metadata, not a fresh verification of vault contents. Rotation lives under Rotate credential… rather than looking like an empty credential field. Disabled/unconfirmed entries do not claim a stored value. Each card includes Recent credential usage (three visible events, up to ten with expansion), filtered to its Space and connection. Times show relative age plus a local calendar label, such as 5 min ago · Today, 5:10 PM or 2 days ago · Sun, Sep 6, 9:50 PM. Hover for the exact local timestamp. This is connection history across rotations, not exact secret-version attribution or usage of the key outside the gateway. Multiple credentials bound to one connection share history. A provider response/HTTP 200 is not necessarily business success; one call can produce multiple audit events. Missing audit configuration, unavailable data and an empty feed are separate states. Each agent grant row also has View activity, an on-demand expansion scoped to that agent and connection, with refresh. External mode also offers a correspondingly filtered gateway link. It shows time, request method/host, named destination, task/run context and recorded outcome—not request bodies or an inferred deployment result. The agent query accepts a Commons agent handle; the API derives the same canonical actor URL as ticket issuance. It validates the gateway’s echoed actor filter and each event, and applies the filter before the ten-event limit. Old/unfiltered responses fail closed. The history API does not require a still-active agent grant, so authorized Space humans can inspect history after revocation; it still requires a credential record and known agent identity. The regular credential card retains all-agent history. Browser requests use the human session, never an audit key. The agent access and human governance forms use the shared member autocomplete (also used for payment access and task offers). Search by display name or @handle, then explicitly choose a result. Typing clears any previous selection; only the selected canonical handle is submitted, not search text or a display name. Candidate lists remain scoped to the existing Space/type rules and all server-side authority checks remain in place. Selecting an agent does not grant access until the human submits Grant access. GET /v0/spaces/:space/credential-control/credentials/:credential/activity requires a live human sign-in session with an explicit Space credential owner role, just like credential metadata. Maintainer, approver, and server roles or the deployment key do not bypass this read boundary. The API resolves the connection from the credential row, ignores client-selected connection filters, and rechecks authority on every read. It returns only allowlisted event metadata (agent, method/host, outcome, time, task/run/purpose), never raw URLs/paths, headers, bodies, fingerprints or secret references. Disabled records retain access to history for authorized humans. In native mode activity reads the Spaces database directly. No audit-reader key or dashboard login is needed. The private page links to the Spaces service on Railway. Railway authentication remains separate; no credentials are included in links. For legacy external mode only, privately configure the Commons API with CREDENTIAL_GATEWAY_URL and CREDENTIAL_GATEWAY_AUDIT_READER_KEY from the existing gateway reader identity. Optional CREDENTIAL_GATEWAY_PUBLIC_URL and CREDENTIAL_GATEWAY_RAILWAY_SERVICE_URL provide external dashboard/service links. Never use NEXT_PUBLIC_ variables or expose these keys to agent clients. The gateway’s /v1/events now accepts connectionId, order=desc, and exclusive before=<sequence> for older pages. Default ascending since behavior remains compatible with audit exporters. New responses echo order/scope; Commons rejects older/mis-scoped responses rather than showing the wrong credential’s history. The UI shows newest events first and paginates older events. Statistics describe the loaded page, not lifetime totals. Its topology remains a deployment snapshot. Verification for this change: local API-to-gateway authorization/isolation, PostgreSQL-compatible query parity using PGlite, memory replay/history tests, redacted DTO and fail-closed tests, and browser checks using synthetic fixtures. Production PostgreSQL performance and live server-only key wiring require a deployment check; no provider call is needed to verify this read-only feature. Commons events record role grants/revocations, connection grants/revocations, credential rotations, exact approvals, approval consumption, and ticket issuance. The Credential Gateway separately records the bounded upstream call. To contain an incident: disable affected connections, revoke the agent’s Commons key and the compromised upstream key at the provider, then inspect both event streams. Replacing a vault value alone does not revoke the old upstream key.