Concepts
The enduring object in Mutual Trust is the relationship between an API, a calling workload and the two organizations responsible for them. Everything else (certificates, policy, evidence) serves that relationship.
The model at a glance
- An organization (the sponsor) creates a trust space, which gets its own private root and issuing CA.
- In the space it creates a service (an API) with route templates (the routes it is willing to share) and pairs one or more gateways.
- It sends an invitation to a partner organization with a scope drawn from the templates.
- The partner accepts, binding a workload. Acceptance creates a grant: the only cross-organization edge.
- The workload enrolls a locally generated key and receives a short-lived certificate whose single URI SAN is its workload identity URI.
- Grant changes produce new policy revisions. Each gateway fetches a signed manifest, enforces it on every request, and returns signed acknowledgments and receipts.
- Each change is an operation you can follow until every gateway has applied it.
Organizations and partners
An organization is a tenant. Every organization has its own members and roles (see Organization and roles). The same organization can be a sponsor of its own services and a partner (consumer) on someone else’s. A partner sees only its side of explicitly shared relationships: the services it was granted, never the sponsor’s other partners.
Organizations have a kind of customer or sample. Sample organizations are created by Try the Orders API and are always labelled “(sample)”. An organization created by an invitation to a new partner starts with no members and is unclaimed until someone accepts the invitation (see claiming).
Trust spaces
A trust space groups services that share a CA hierarchy in one environment (sandbox or production). Creating a space provisions, through the isolated signer:
- a private root (path length 1), the trust anchor whose fingerprint the portal shows;
- an online issuing CA (path length 0) that signs workload leaves;
- a separate manifest-signing key used only to sign policy manifests.
There is no trust between spaces or between customers. Space names are unique per organization and environment.
Services and route templates
A service is an API behind one or more of your gateways. Its route templates are the upper bound of what you can ever grant on it; every grant’s scope must fit inside them. A template has a name, methods and a path, and may be flagged restricted as a label for humans (the flag does not change matching).
[
{
"name": "Read orders",
"methods": [
"GET"
],
"path": "/orders/*"
},
{
"name": "Submit orders",
"methods": [
"POST"
],
"path": "/orders"
},
{
"name": "Admin export (restricted)",
"methods": [
"GET"
],
"path": "/admin/*",
"restricted": true
}
]How routes match
| Rule | Behaviour |
|---|---|
| Exact path | /orders matches only /orders (a single trailing slash is ignored). |
| Wildcard | /orders/* matches /orders and every descendant such as /orders/1001/lines, but not /orders-archive. |
| Methods | One or more of GET HEAD POST PUT PATCH DELETE OPTIONS. A HEAD request is allowed wherever GET is granted. |
| Query string | Ignored for matching. |
| Ambiguous paths | Refused (deny ambiguous_route): encoded %2F, %5C, %00 or %2E, backslashes, . or .. segments, empty segments, or invalid percent-encoding. |
| Allowed characters | Rule paths match ^/[A-Za-z0-9._~\-/]*(/\*)?$, at most 200 characters, with no // or dot segments. |
Invitations
An invitation offers a scoped grant on one service to one intended organization. It carries a one-time secret (mtinv_…) that is shown once to the sponsor and stored only as a hash. It expires after expires_in_hours (default 168 hours, maximum 720). Redeeming it is atomic: two concurrent acceptances yield exactly one grant.
| State | Meaning | Next |
|---|---|---|
pending | Sent, not yet answered. | accepted, declined, canceled, expired |
accepted | Redeemed; a grant exists. | None (terminal) |
declined | The recipient declined with the secret. | None (terminal) |
canceled | The sponsor withdrew the invitation. | None (terminal) |
expired | Past expires_at without an answer. | None (terminal) |
Grants and the grant state machine
A grant authorizes one workload of the consumer organization to call specific routes and methods of one sponsor service. It has a scope ({"v":1,"routes":[…]}), an optional expires_at, a state and an integer revision that increases on every change. Changes require If-Match with the current revision.
| State | Enforcement | Capacity | Transitions to |
|---|---|---|---|
pending | Denied. | None. | active, revoked, expired |
active | Allowed on its scope. | Consumes a production connection. | suspended, revoked, expired |
suspended | Denied (grant_suspended), reversible. | Slot stays reserved. | active, revoked, expired |
revoked | Denied; every live leaf revoked; re-enrollment blocked. | Released. | None (terminal) |
expired | Denied (grant_expired). | Released. | None (terminal) |
Current build
Reason codes for suspend and revoke are contract_ended, security_incident and owner_request.
Workloads and workload identity
A workload is the partner’s calling software: a service, job or pod. It belongs to the consumer organization and is bound to an attestation at acceptance time:
sandbox_token: sandbox only. A partner admin approves enrollment in the browser and the CLI redeems a single-use token.oidc_jwt: an exact issuer, audience and subject from an OIDC workload identity provider the consumer organization approved, such as a Kubernetes service account. Required in production.
Every certificate issued to a workload carries exactly one URI SAN, its workload identity URI:
urn:mutualtrust:workload:<environment>:<consumer org id>:<workload id>
urn:mutualtrust:workload:sandbox:org_01jcqa7m3a2b7c9d4e6f8g1h3j:wl_01jcqb3p1y2z4a6b8c0d2e4f6gGateways authorize on this URI and the issuing CA, never on the subject CN or O, which are informational. The URI’s environment must match the gateway’s environment.
Issuers and the CA hierarchy
| Issuer | Profile | Constraints | Sandbox validity |
|---|---|---|---|
| Root | mt.ca.root.v1 | CA, pathLen 1, keyCertSign + cRLSign | 90 days |
| Issuing CA | mt.workload.clientauth.v1 | CA, pathLen 0, keyCertSign + cRLSign | 60 days |
| Workload leaf | mt.workload.clientauth.v1 | CA:false, digitalSignature, clientAuth | 24 hours |
Subjects read “Mutual Trust SANDBOX TEST Root · <space>” / “… Issuing CA · <space>” with O = your organization and OU = the space ID. Leaf validity is bounded by the grant’s expiry and the issuing CA’s validity. Export the public chain with GET /v1/spaces/{id}/trust-bundle.pem. The full leaf profile is in Wire formats.
Policy revisions and epochs
The control plane compiles a policy document per service from authoritative state: trusted issuing CAs, one rule per active grant and issuer, and a deny set of suspended or withdrawn grants and revoked serials that are still within validity. Each distinct document gets the next (epoch, revision) for the service; publishing is skipped when nothing changed, so revisions only move when enforcement would.
- Revision increases monotonically within an epoch. Gateways reject a lower revision (downgrade) and a same-revision manifest with a different hash (replay conflict).
- Epoch starts at 1 and changes only through an authenticated recovery action (for example after a database restore), so that gateways never accept an older state.
- The
policy_hashis the SHA-256 of the canonical JSON of the policy document.
Manifests
A manifest is the policy document wrapped in a compact JWS (ES256, typ: mt.manifest.v1+jws) signed with the space’s manifest key and scoped to one tenant, environment, service and gateway. It is valid for 300 seconds after issue. Gateways refresh every 15 seconds, accept a cached manifest until exp plus a 30-second clock allowance, and then fail closed. The verification key is pinned during pairing. See Manifest JWS.
Receipts
For every request a gateway signs a receipt (mt.receipt.v1+jws) with its own pairing key. A receipt records two separate verdicts, TLS (verified, failed or absent) and authorization (allow or deny with a reason), plus the gateway, policy revision, grant, principal, serial, method, normalized route and time. Request bodies, headers and payloads are never sent.
Operations: configured, delivered, applied
Every grant or certificate change returns an operation_id. GET /v1/operations/{id} reports progress per paired gateway:
| State | Meaning |
|---|---|
| Configured | The change is committed in the control plane (effective_control_plane_time). The operation is publishing until a policy revision that includes it exists. |
| Delivered | The gateway fetched a manifest at or above the target revision (delivered_at). |
| Applied | The gateway returned a signed acknowledgment that it applied that revision (applied_at, apply_latency_seconds). |
| Offline (pending) | The gateway has not checked in for 5 minutes. It will fail closed when its cached manifest expires. |
| Verified | For a service as a whole: the readiness checklist has seen a real allowed request and a denied control request with verified TLS. |
Evidence is per gateway