Entity reference
Target audience: Solution Architects, Enterprise Architects, Platform Implementers
Reading time: 25 minutes (full read), or jump to a specific entity
Prerequisites: The six-layer model
Introduction
This page is the canonical reference for the thirteen entities documented here: the twelve entities of the deployment organization model, plus the single control-plane orchestration entity that sits above it. Each entity definition includes its purpose, key attributes, lifecycle considerations, and concrete examples.
For the layered structure that groups these entities, see The six-layer model. For their relationships and runtime composition, see Relationships and runtime flow.
Organizational entities
Deployment
A Deployment is a complete installation of ANKASecure©. It defines:
- The infrastructure footprint (compute, network, storage)
- The global compliance baseline (regulatory region, certification posture)
- HSM and KMS integrations (vendor, slots, partitions)
- Default cryptographic policies inherited by tenants
A Deployment maps to a productive environment, a regulatory region, or a logical infrastructure boundary. Organizations operating under conflicting regulatory regimes — for example, jurisdictions with incompatible cryptographic mandates — typically run separate deployments.
Key attributes:
| Attribute | Description |
|---|---|
| Compliance region | The regulatory frame (BSI, NIST, ANSSI, ETSI, EU, UNRESTRICTED) |
| HSM provider | The cryptographic substrate (PKCS#11, AWS CloudHSM, Thales Luna, SoftHSM) |
| Default policies | The baseline algorithm catalog and lifecycle rules |
| Environment classification | Production, disaster recovery, development |
When to define a separate deployment: when the regulatory or jurisdictional context differs in ways that require independent governance. A single multinational organization may run multiple deployments — one per major regulatory region — to satisfy data residency and sovereign cryptography requirements.
Tenant
A Tenant is an isolated cryptographic domain within a Deployment. It provides:
- Boundaries of key ownership
- Independent policy scope (within deployment baseline)
- Administrative separation
- Audit isolation
Each tenant owns its cryptographic assets and defines its own governance rules within the constraints imposed by the deployment. A compromise within one tenant cannot reach assets in another tenant, even when both run on the same infrastructure.
Key attributes:
| Attribute | Description |
|---|---|
| Tenant identifier | UUID, stable across the tenant's lifetime |
| Display name | Human-readable name (Core Banking, Digital Channels, Risk & Compliance) |
| KEK label | Reference to the tenant's Key Encryption Key in the HSM |
| Inherited policies | Policies inherited from the deployment baseline |
| Local policy overrides | Tenant-specific policy overrides within allowed ranges |
Multi-tenancy scope: a Tenant is the unit of cryptographic isolation. It is also the natural unit of administrative delegation — different tenants can be administered independently. For details on the technical isolation mechanisms, see Multi-Tenant Isolation Model.
Application
An Application is a business system or functional domain. It defines:
- Business ownership
- Risk classification
- Regulatory scope
- Operational context
Examples: Core Banking, Card Issuance, CRM, Collections, Regulatory Reporting.
Key attributes:
| Attribute | Description |
|---|---|
| Application identifier | UUID |
| Display name | Human-readable name |
| Owning tenant | The tenant within which this application operates |
| Business unit | Organizational owner (Treasury, Digital Division, Risk) |
| Cost center | Financial accountability tag |
| Risk level | CRITICAL, HIGH, MEDIUM, LOW |
| Regulatory scope | Regulations applicable (SOX, PCI-DSS, GDPR, AML, eIDAS 2) |
| Data classification | RESTRICTED, CONFIDENTIAL, INTERNAL, PUBLIC |
| Application type | INTERNAL, EXTERNAL, HYBRID |
Important distinction: Applications are not technical identities. They do not authenticate, do not hold credentials, and do not invoke operations directly. They are the business container under which technical actors operate. The technical identity is the Cryptographic Actor.
Application status is a runtime control over its actors. While an Application never authenticates itself, its status is a runtime governance lever, not mere metadata. Effective status is the logical AND over the containment chain tenant ∧ application ∧ actor (within an ACTIVE deployment): an actor may authenticate and operate only when its owning tenant, its owning application, AND the actor itself are all ACTIVE. Suspending an Application therefore immediately blocks every actor it contains — their M2M authentication is denied at token issuance (reason APPLICATION_NOT_ACTIVE), and any already-issued, still-valid JWT is denied at the Core API crypto seam and on scope-gated metadata reads (listKeys / viewKeyMetadata) with reason application-suspended.
Application status follows the same reversible/terminal distinction as the actor (below):
- SUSPENDED — reversible; the application (and therefore its actors) returns to service on reactivation. Suspension is a status flag on the application alone — it rewrites no child entity. The contained actors, grants, exchange contexts, exchanges, and keys are left untouched; effective status is re-composed by the AND over the containment chain at read time, and reactivation restores service instantly because nothing below was mutated.
- DISABLED — terminal; a DISABLED application cannot be reactivated. DISABLE does not revoke the actors' credentials (credential revocation is a separate, explicit lifecycle act).
Revoking an Application is a terminal, irreversible cascade (distinct from SUSPEND). The revoke requires a reason. Where SUSPEND rewrites no child, deleting an Application (POST /api/v3/admin/tenants/{tenantId}/applications/{applicationId}/revoke, HTTP 204) drives every entity the application contains to its terminal state in a single best-effort cascade. There is no refuse-if-dependents guard: the presence of active Actors, Exchange Contexts, Cryptographic Exchanges, or Internal Crypto Use Cases does not block the delete. The cascade runs in dependency order:
- Resolve the application's owned key identifiers (KIDs) first.
- Revoke the application's Internal Crypto Use Cases.
- Revoke the associated Capability Grants.
- Close the Exchange Contexts.
- Revoke the Cryptographic Exchanges (status
REVOKED, soft-deleted). - Disable the Cryptographic Actors — including the application's non-deletable Default Actor, which is cascade-disabled here (its
isDefaultflag does not block the cascade). - Archive the application's non-shared keys.
Nothing is physically destroyed. The application's keys move to ARCHIVED — their material is retained, never destroyed. A key that is shared with another still-active aggregate is not archived; it keeps its real current lifecycle status. Each key is retired according to its current status and is never revoked twice: an active or expired key is revoked for ADMINISTRATIVE and archived; a suspended key is revoked and archived — for SECURITY_COMPROMISE when that is its suspension cause, so a key held for a security compromise stays sealed, and for ADMINISTRATIVE otherwise; a revoked key is archived keeping its cause; an archived or pending_destruction key is left as it is. After the cascade, the retired application and its terminal children are removed from the active listings but remain inspectable through a read-only retired-applications surface (see the admin-api endpoints documentation). The terminal revoke cascade is what makes the application.deleted propagation on the Cryptographic Actor and Cryptographic Exchange reachable — SUSPEND never reaches it.
Operational identity entities
Cryptographic Actor
A Cryptographic Actor is the operational subject that executes or requests cryptographic operations. It represents:
- An internal API service
- A batch processing job
- An integration channel
- A third party (for example, a card embossing vendor)
- A BPO processing system
Each Cryptographic Actor belongs to an Application, operates under defined constraints, and is the primary subject of authorization. Authorization decisions, audit events, and rate-limit enforcement are anchored on the actor.
Actors are the unit of trust at runtime. The model treats internal services and external parties uniformly: both are actors with clearly delimited capabilities.
Key attributes:
| Attribute | Description |
|---|---|
| Actor identifier | UUID |
| Owning application | The Application this actor operates within |
| Actor type | One of six: INTERNAL, PARTNER, SERVICE, THIRD_PARTY, REGULATORY, RAPIDAPI |
| Status | One of three: ACTIVE, SUSPENDED, DISABLED |
| Display name | Human-readable name (visa-embossing-actor, fraud-detection-service) |
The six actor types, which are the complete persisted set:
| Type | Means |
|---|---|
INTERNAL | Organization-owned service or process |
PARTNER | Allied organization with a trust relationship (embedded partner) |
SERVICE | Automated batch, scheduled job, or background processor |
THIRD_PARTY | External vendor integrating via published interfaces |
REGULATORY | Auditor or regulator; typically verify-only capability grants |
RAPIDAPI | Marketplace user auto-provisioned by the RapidAPI bridge; plan tier is carried by the attached Constraint Policy, not by a separate actor attribute |
Actor lifecycle: an actor is created when an integration is established, suspended when it is paused (without losing its authorizations), and disabled when the integration is terminated. Suspending the actor immediately blocks all of its grants without deleting them; reactivation restores the previous state. There is no REVOKED actor state — termination is DISABLED, and it is terminal.
Actor status is a runtime control (effective-status containment). The actor is the lowest leg of the effective-status containment chain tenant ∧ application ∧ actor (within an ACTIVE deployment). Suspending the actor — independently of its application's status — makes it effectively non-ACTIVE, which blocks:
- M2M authentication — the
client_credentialsgrant is denied at token issuance (reasonACTOR_NOT_ACTIVE). - Cryptographic operations — an already-issued, still-valid JWT is denied at the Core API crypto seam.
- Scope-gated metadata reads —
listKeys/viewKeyMetadataare denied at the seam with reasonactor-suspended(a status denial, neverNO_GRANT). - The interoperability and migration planes —
interoperability.encrypt,interoperability.verifySignature,migration.convertPkcs7andmigration.analyzePkcs7are denied at the same seam, across six handlers: each PKCS#7 operation carries a streaming twin on the same scope. The interoperability operations andmigration.analyzePkcs7use no tenant key and take no Capability Grant decision, so for them the containment check is the only thing that closes them. A PKCS#7 → JOSE conversion (migration.convertPkcs7, buffered and streaming) does use tenant keys: after the containment check it also takes a Capability Grant decision per leg —REENCRYPTfrom the opening recipient to every recipient (its own pair included) andRESIGNfrom every signer to itself — so a suspended actor is refused before any grant is charged.
The two control states are:
- SUSPENDED — reversible. The actor pauses without losing its grants or credentials; reactivation restores its effective status.
- DISABLED — terminal. A DISABLED actor cannot be reactivated (suspend/reactivate of a DISABLED actor returns HTTP 422). DISABLE does not revoke the actor's credentials — credential revocation is a separate, explicit lifecycle act.
The control is enforced at two points (defense-in-depth): the admin-api token-issuance gate (which denies the grant unless the whole containment chain is ACTIVE) and the core-api crypto seam (a fail-closed resolver that re-checks the live effective status on every operation and scope-gated read, so a JWT minted while the actor was ACTIVE stops working the moment the actor is suspended).
Default Actor and cascade-disable on application.deleted. Each Application auto-provisions a non-deletable Default Actor (isDefault = true) that cannot be deleted independently — an attempt to delete it on its own is refused. When its owning Application is terminally deleted, however, the Default Actor — like every other actor the application contains — is cascade-disabled as one step of the application's revoke cascade; isDefault does not exempt it. This is the distinction the cascade resolves: the disable happens because the parent Application is being terminally deleted, not as an independent actor delete. See Application for the full ordered cascade.
Credential
A Credential is the authentication mechanism associated with a Cryptographic Actor. The platform persists three kinds, recorded on actor_credentials.kind:
CLIENT_SECRET— an OAuth 2.0client_credentialspair: aclient_idand a secret held only as a hash. The plaintext is returned once, at issuance or rotation, and never again.PRIVATE_KEY_JWT— a registered public key (JWK). The actor proves possession of the private half by signing a client assertion; the platform stores no secret at all.FEDERATED_JWT— the actor is bound to an external issuer and subject, and authenticates with a token that issuer minted. Federated identity therefore applies to Cryptographic Actors as well as to human users.
A Cryptographic Actor may hold multiple credentials simultaneously (for credential rotation), and credentials have independent lifecycle management. The separation between actor and credential allows secret rotation without altering authorization grants.
Key attributes:
| Attribute | Description |
|---|---|
| Credential identifier | UUID |
| Owning actor | The actor this credential authenticates |
| Mechanism | One of CLIENT_SECRET (a client_id + hashed secret pair), PRIVATE_KEY_JWT (a registered public JWK, no stored secret) or FEDERATED_JWT (a binding to an external issuer and subject). Actor-level mTLS is not implemented |
| Status | One of three: ACTIVE, ROTATING_OUT, REVOKED |
| Version | Monotonic counter, incremented on each rotation |
| Valid from / valid until | Validity window |
| Revoke at | Timestamp at which a ROTATING_OUT credential transitions to REVOKED (set by the rotation grace-window logic; null for ACTIVE and REVOKED rows) |
Credential rotation pattern: an actor can hold an ACTIVE credential alongside a ROTATING_OUT one during a rotation window. The new credential is issued and tested in parallel with the old one, which keeps authenticating until its revoke_at elapses. No grant changes are required.
Disabling the actor does not revoke its credentials. The two are independent lifecycle acts, which is exactly why a deprovisioning cascade revokes the credential explicitly rather than relying on the actor transition to do it.
ROTATING_OUT intermediate state: when a rotate operation specifies a positive graceWindowHours (default 24h, max 720h), the previous credential transitions from ACTIVE to ROTATING_OUT with revoke_at = now + graceWindowHours. A ROTATING_OUT credential continues to authenticate until revoke_at elapses — this is the entire point of the grace window. A scheduled finalization task in ankasecure-admin-api sweeps ROTATING_OUT rows on a fixed cadence (default 60s) and transitions them to REVOKED when their revoke_at is in the past. When the rotate operation specifies graceWindowHours = 0 (INLINE hard cutover), the previous credential transitions directly to REVOKED in the same transaction as the new credential's INSERT — no ROTATING_OUT row is ever observable by a concurrent reader.
Governed exchange entity
Exchange Context
An Exchange Context is a governed business scenario of the tenant in which cryptographic operations take place. It models:
- The purpose of the exchange
- The sensitivity of the data exchanged
- The regulatory regime the exchange operates under, when one applies
- A lifecycle that every grant in it follows
Examples:
- Card Embossing — VISA
- Card Embossing — AMEX
- Collections Processing — BPO Alpha
- Regulatory Reporting — Central Bank
- Internal Analytics Pipeline
A context names no application, counterparty or direction. Those belong to the Cryptographic Exchanges created in it: each Exchange declares its own origin and destination. One context can therefore hold an outbound Exchange, an inbound Exchange and an Exchange between two of the tenant's own applications, governed and suspended together.
Key attributes:
| Attribute | Description |
|---|---|
| Context identifier | UUID |
| Owning tenant | The tenant the context belongs to |
| Name | A slug, unique among the tenant's live contexts |
| Display name, description | Human-readable identity |
| Business purpose | The reason the exchange exists |
| Sensitivity level | PUBLIC, INTERNAL, CONFIDENTIAL or RESTRICTED; INTERNAL when omitted |
| Regulatory regime | Optional free text of at most 120 characters, such as PCI DSS 4.0 |
| Status | One of four: PENDING_ACTIVATION, ACTIVE, SUSPENDED, CLOSED |
| Owning use case | ownedByUseCaseId: the Internal Crypto Use Case that provisioned the context, or null when the tenant owns it. A use case's context admits only that use case's grants (409 exchange-context-owned-by-use-case for any other Exchange or grant) |
Every Capability Grant belongs to exactly one Exchange Context. A grant cannot be created without one, whoever owns it, and it authorizes only in that context. A context that is not ACTIVE accepts no new grant and no new Exchange (409 exchange-context-not-active).
Why this entity exists: the Exchange Context distinguishes ANKASecure© from a traditional KMS. By modeling the business interaction explicitly, authorization decisions can reflect not only who is acting and what they are doing, but the governed business relationship in which the action takes place.
The four states:
| Status | Means |
|---|---|
PENDING_ACTIVATION | Composed but not yet in force; no grant in it authorizes |
ACTIVE | In force |
SUSPENDED | Every capability grant in this context is denied, whatever owns it. No token is blacklisted: suspension writes the new status, publishes it and updates the authorization projection, so an already-issued token keeps authenticating and is refused at the capability decision — a fail-closed re-resolution, not a token revocation. Reversible |
CLOSED | Permanent. Closing revokes every Cryptographic Exchange in the context that is not already terminal, retires every live grant in it whatever owns the grant, and refuses the next request through any of them. A closed context cannot be resumed and accepts nothing new; its grants, Exchanges and audit trail stay readable. The close is recorded as an exchange-context.closed event and an exchange-context.updated event for the status change, and every grant and Exchange it ended carries its own event naming the close as its cause. A context that an Internal Crypto Use Case* provisioned is closed only by revoking that use case (409 exchange-context-owned-by-use-case otherwise) |
Lifecycle implication: when a third-party relationship is renegotiated or terminated, the cryptographic implications propagate through the affected Exchange Context. Suspending or closing a context affects every grant in it — without requiring per-grant inspection.
Cryptographic resource entities
Cryptographic Asset
A Cryptographic Asset is managed cryptographic material. Types include:
| Type | Examples |
|---|---|
| Symmetric keys | AES (128, 192, 256), ChaCha20, Camellia |
| Asymmetric key pairs | RSA, EC (P-256, P-384, P-521) |
| Post-quantum keys | ML-KEM-512/768/1024, ML-DSA-44/65/87, SLH-DSA, Falcon |
| Hybrid keys | Composite PQC + classical pairs |
| Verification keys | Public material derived from private keys |
| Wrapping keys | KEKs used to protect other assets |
Each asset belongs to a Tenant, is governed by policy, and follows a lifecycle that is two-dimensional: a stable alias (the KID identity) and the material versions underneath it each carry their own status, and what the API publishes as the key's status is a projection of the pair. The nine published tokens and how they are produced are set out in Asset lifecycle below.
Key attributes:
| Attribute | Description |
|---|---|
| KID (Key Identifier) | Stable human-readable identifier (payment-signing-key-001) |
| Internal UUID | System-generated unique reference |
| Owning tenant | The tenant that owns this asset |
| Algorithm | The cryptographic algorithm (ML-DSA-87, AES-GCM-256) |
Key type (kty) | One of the twenty-one published key-type tokens — ML-KEM, HQC, FRODO, CMCE, BIKE, SABER, NTRU, NTRUPRIME, SMAUG-T, ML-DSA, SLH-DSA, FALCON, XMSS, LMS, EC, RSA, oct, OKP, SM2, GOST-EC, COMPOSITE. A hybrid asset publishes the single coarse token COMPOSITE |
Composite construction (compositeMode) | For a hybrid asset only — COMPOSITE_KEM_COMBINE or COMPOSITE_SIGNATURE. The construction is a separate axis from the key type; it is never a kty value |
| Permitted operations | The capabilities this asset supports |
| Lifecycle status | The projected status — one of the nine published lower-case tokens. Seven are reachable at the kid level; see Asset lifecycle |
| Created at | Timestamp |
| Expires at | Timestamp (if time-limited) |
| Usage count | Cumulative operation counter |
| Max usage limit | Hard limit (optional) |
| Origin | GENERATED, IMPORTED — always stated on every material; a version created by rotation is GENERATED |
| Exportable | Boolean, always stated on every material (typically false in production); a rotation keeps the key's current value unless it names a new one |
| KEK version | Reference to the wrapping key version |
Asset identification: assets are referenced by stable KID. The runtime resolves the appropriate version based on policy, lifecycle state, and operation type. This is the foundation of the KID system.
Asset lifecycle — two dimensions, one projection
An asset is not a row with a status column. It is a stable alias plus the material versions recorded under it, and each dimension has its own state machine. Rotation advances the material while the alias — and therefore the KID every grant and every ciphertext refers to — stays constant. That is the whole point of the KID system, and it is why a flat single-status reading of a key is wrong.
Dimension 1 — the alias (the KID identity). Four states:
(creation) -> ACTIVE
ACTIVE -> SUSPENDED (incident investigation)
SUSPENDED -> ACTIVE (incident cleared)
ACTIVE -> RETIRED (decommission or revoke)
SUSPENDED -> RETIRED (compromise confirmed)
RETIRED -> DELETED_LOGICAL (retention elapsed + operator delete)
Dimension 2 — each material version. Six states, with at most one PRIMARY per alias at any time:
PENDING_PRIMARY -> PRIMARY (atomic rotation activation)
PENDING_PRIMARY -> DESTROYED (operator cancels the rotation)
PRIMARY -> RETIRED (a rotation supersedes it)
PRIMARY -> REVOKED (alias revoke cascade)
RETIRED -> REVOKED (operator revokes this material)
RETIRED | REVOKED -> PENDING_DESTRUCTION (approval of a destruction request)
PENDING_DESTRUCTION -> RETIRED | REVOKED (cancel-destruction restores the prior state)
PENDING_DESTRUCTION -> DESTROYED (cooling-off elapsed, the platform's destruction executor)
The projection. The published key status is computed from the alias and its representative material — the current PRIMARY when the key has one, otherwise (a revoked, archived or pending-destruction key) a deterministic representative of its non-destroyed versions, preferring a PENDING_DESTRUCTION version and then the highest version number. The key list, the single-key read and the lifecycle guards all use this one rule, so they never disagree on a key's status. It is evaluated strictly in this order:
| # | Condition | Projects |
|---|---|---|
| 1 | alias DELETED_LOGICAL | deleted_logical |
| 2 | alias SUSPENDED | suspended |
| 3 | alias RETIRED, material PENDING_DESTRUCTION | pending_destruction |
| 4 | alias RETIRED, archived_at not set | revoked |
| 5 | alias RETIRED, archived_at set | archived |
| 6 | alias ACTIVE, material PENDING_DESTRUCTION | pending_destruction |
| 7 | alias ACTIVE, material REVOKED | revoked |
| 8 | alias ACTIVE, material past its expiresAt | expired |
| 9 | alias ACTIVE, otherwise | active |
Two consequences of the table that a flat list conceals:
archivedis not a persisted status. It is a derived label: an alias inRETIREDwhosearchived_atis set. Archiving is the act of recordingarchived_at/archived_by— which is why archiving is only reachable fromrevoked, and whyarchivedandrevokedare the same alias state distinguished solely by that timestamp. Both carry the revocation cause: aRETIREDalias always has one, and archiving keeps it.pending_rotationandrotatedare never emitted at the kid level. They belong to the material dimension, where the corresponding states arePENDING_PRIMARYandRETIRED. The projection above has no branch that can produce either token for a key, so no client should switch on them.
Archiving is not data-denying — the cause is. Archive keeps the cause the key was revoked for: under the default lifecycle policy an archived key revoked for any cause other than SECURITY_COMPROMISE still permits decrypt and verify, while one revoked for SECURITY_COMPROMISE stays sealed on every plane; what archiving itself stops permitting is encrypt and sign. This is the same semantics AWS, GCP and Azure KMS apply to a disabled or retired key, and it is what makes a decrypt-only authorization over an archived key a legitimate data-recovery shape rather than a misconfiguration.
The destruction chain and its cooling-off gate
Destroying key material is a deliberate, multi-step, temporally gated cycle. It is never a side effect of another action, and no administrative action on an authorization construct — deleting a grant, revoking a use case, deleting an Application — enters this chain at any point.
stateDiagram-v2
[*] --> active
active --> revoked: POST /revoke (cause recorded)
revoked --> archived: POST /archive (cause kept, archivedAt set)
archived --> pending_destruction: POST /mark-for-destruction<br/>(sets destructionEligibleFrom = now + coolingOffDays)
pending_destruction --> archived: POST /cancel-destruction
pending_destruction --> deleted_logical: DELETE<br/>(only after destructionEligibleFrom)
deleted_logical --> [*] Each transition is guarded, and the guards are the point:
| Step | Precondition | Effect |
|---|---|---|
revoke | projected status is active, expired or suspended — refused 409 from revoked, archived and pending_destruction (Key already revoked: <kid> is <status>.), and from a key suspended for SECURITY_COMPROMISE when the revoke names another cause; a destroyed key is not found (404) by the live-key lookup | Alias RETIRED with a revocation cause; non-destroyed materials REVOKED with that cause, except a version already revoked for SECURITY_COMPROMISE, which keeps its cause and reason |
archive | projected status is revoked | archived_at / archived_by recorded on the alias, which keeps its revocation cause; the other material causes are cleared and those materials move to RETIRED, except a version revoked for SECURITY_COMPROMISE, which stays REVOKED with its cause and reason so it keeps refusing decrypt and verify — the key now projects archived |
mark-for-destruction | projected status is archived | Opens a quorum request that a person other than the requester approves; the approval moves every non-destroyed material to PENDING_DESTRUCTION and schedules a destruction order for the approval plus coolingOffDays (7–365; the tenant lifecycle policy's default, else the deployment's, else 30) |
cancel-destruction | projected status is pending_destruction | Materials revert to their prior status (REVOKED if the material carries a cause, otherwise RETIRED), so a version revoked for SECURITY_COMPROMISE is REVOKED with that cause again; the alias keeps its cause and archived_at, so the key projects archived again |
| destruction (the platform) | the order's cooling-off has ended and its versions are still the approved ones | Materials DESTROYED; alias DELETED_LOGICAL; the Key ID is never reused. An order whose versions changed ends FAILED and destroys nothing |
The cooling-off window is a temporal gate, not advice: no caller can destroy the key, and the platform destroys it only once the window has ended. The window exists so that an irreversible act has a period in which it can be reconsidered — which is also where a warning belongs when other constructs still reference the key.
DELETED_LOGICAL is the end of the published lifecycle: the KID cannot be revived, and no authorization may be composed over it. The identity is however retained for audit, and because the uniqueness constraint on a KID applies only to live aliases, the same KID may be created again afterwards — a fresh key with a fresh identity that happens to reuse the name.
Purpose projection on import (Origin = IMPORTED): every Asset has a single KeyPurpose (ENCRYPT_DECRYPT or SIGN_VERIFY) — its permitted operations never span both families. When an Asset originates from a legacy JKS/PKCS#12 import, its purpose is derived from the certificate X.509 KeyUsage or declared explicitly; an ambiguous key cannot be imported as a single dual-capable Asset. A genuinely dual-use legacy keypair is projected onto two single-purpose Assets (<kid>-enc and <kid>-sig) over the same key material, each with its own material version. The precedence that decides an alias's purpose is declared on the import request itself, POST /api/v3/admin/tenants/{tenantId}/keys/import-keystore.
Capability
A Capability is a cryptographic function that can be executed. Capabilities abstract the use of cryptographic material into controlled operations.
Standard capabilities:
| Capability | Operation | Typical use |
|---|---|---|
encrypt | Encrypt data with a key | Protecting data at rest or in transit |
decrypt | Decrypt data with a key | Unwrapping protected data |
sign | Produce a digital signature | Authenticating data origin |
verify | Verify a digital signature | Validating data origin |
reencrypt | Decrypt and re-encrypt with the same or different key | Algorithm rotation, KEK rotation |
resign | Verify and re-sign with a different key | Signature rotation |
wrap, unwrap and derive are not grantable
Core API has no corresponding endpoints, so a grant carrying one could authorize nothing; they are not in the persisted vocabulary. The six above are the whole set a grant may carry.
Why capabilities matter: a consumer never receives a key; it requests the execution of a capability and receives the operational result. This abstraction enables crypto-agility — the underlying algorithm or key version can change without affecting consumers.
For the broader principles behind this design, see Policy-Driven Crypto-Agility.
Control entities
Capability Grant
A Capability Grant is the authorization given to a Cryptographic Actor to invoke one or more capabilities under specific conditions. It binds:
- An Actor
- A set of Capabilities — the field is a list
- The Cryptographic Assets it covers: one Key ID for basic capabilities, or ordered key pairs for a transformation capability
- Exactly one Exchange Context, always
- Exactly one owner
- Optionally, a version of a Constraint Policy
Two shapes, never mixed. A basic grant carries one to four of ENCRYPT, DECRYPT, SIGN and VERIFY over one Key ID. A transformation grant carries exactly one of REENCRYPT and RESIGN, alone, over 1 to 32 ordered key pairs source → target. A pair authorizes its own direction only: a REENCRYPT grant from K1 to K2 authorizes re-encryption from K1 to K2, and refuses K2 to K1 and K1 to K1. The pair K1 → K1 is valid on its own and authorizes an in-place re-encryption onto the key's current version after a rotation. REENCRYPT does not decompose into, or imply, a standalone DECRYPT on the source key. An orchestrated use case declaring [ENCRYPT, DECRYPT] emits two grants, one carrying ["ENCRYPT"] and one carrying ["DECRYPT"].
One owner, the same rules for every owner. A grant is owned by a Cryptographic Exchange (EXCHANGE, and it authorizes only while that Exchange is ACTIVE), by an Internal Crypto Use Case (INTERNAL_USE_CASE), by the marketplace plan (MARKETPLACE), or by nobody but the tenant (INDEPENDENT, a grant an operator created directly).
No two live grants of an actor overlap. Two grants overlap when their capabilities intersect and their resources intersect: a shared Key ID, a shared ordered pair, or a wildcard on either side. Creating a grant that would overlap a live grant of the same actor is refused, in whatever Exchange Context either grant lives (409 capability-grant-overlap). Every request an actor makes is therefore authorized by at most one grant, so the request needs no context to be resolved: when the actor's token names no context, the one matching grant decides in its own context; when the token names a context, a matching grant in another context is refused.
One usage budget per grant. A grant that carries several capabilities has one usage count. Under a policy version with maxUsage or revokeOnUse, every operation the grant authorizes, whatever its capability, draws on that one count.
A Capability Grant answers four questions in a single record:
- Who can act? — the Actor
- What can they do? — the Capability
- On what assets? — the Cryptographic Assets
- Under what conditions? — the Constraints, in its Exchange Context
Key attributes:
| Attribute | Description |
|---|---|
| Grant identifier | UUID |
| Subject actor | The Cryptographic Actor receiving the authorization |
| Capabilities | An array: one to four basic capabilities, or exactly one transformation capability |
| Target assets | One Key ID (assetKids), or ordered key pairs (keyPairs). In a STANDARD tenant an operator may instead grant basic capabilities over every key of the tenant (allowWildcardAsset); the marketplace plan's grants are wildcard grants too. A grant an Exchange or a use case owns is never a wildcard |
| Exchange Context | Required: the one context the grant authorizes in |
| Owner | EXCHANGE, INTERNAL_USE_CASE, MARKETPLACE or INDEPENDENT, and the owning Exchange or use case |
| Constraint Policy | The policy version the grant is pinned to (constraintPolicyVersion), whose limits it enforces (constraintSnapshot). The pin changes only by an explicit re-pin, recorded in the grant's assignment history (versions and re-pin) |
| Status | None — a Capability Grant carries no status column. A live row is the authorization; revocation is a soft delete (deleted_at), after which the grant authorizes nothing. Suspension and expiry are properties of the grant's owner, its Exchange Context or its Constraint Policy, never of the grant row |
| Consumption | Present only when the grant was used up: when its single use was taken or its maxUsage reached (consumedAt, useCount, cause = REVOKED_ON_USE or MAX_USAGE). There is no live use count before that: the count is held by core-api's ledger, and admin-api learns it only from the consumption. A consumed grant is retired and authorizes nothing more, and is still readable by its id (getCapabilityGrant returns it with its consumption; a revoked or expired grant reads 404) |
| Effective state | { usable, reason } on every grant read: whether the governance objects the administration plane owns let the grant authorize at the instant of the read, and the first reason they do not. It does not evaluate key lifecycle, rate windows or usage counts, which the cryptographic service decides per request |
| Issued at | Timestamp |
| Issued by | Identity of the granting principal |
Why this is the unit of authorization: revoking a grant removes the actor's ability to invoke the capability without affecting other grants or other actors, and it also removes the operation scope the grant derived into that actor's tokens — no role row is written, so nothing has to be unwound. Removal is bounded rather than instant: up to the 900 s access-token lifetime, plus up to 300 s of enforcement-plane grant cache. The grant is the smallest unit of fine-grained governance.
Constraint Policy
A Constraint Policy expresses restrictions on how a capability can be used. It is a named, reusable tenant-level entity with five enforced dimensions, not a typed parametric object — a single policy version carries all five, and any subset of them may be left unset.
| Dimension | Fields | Effect |
|---|---|---|
| Time window | validFrom, validUntil | The grant authorizes only within the period |
| Weekly schedule | scheduleWeekdays, scheduleStart, scheduleEnd | The grant authorizes only on the listed ISO weekdays (1 = Monday to 7 = Sunday), from scheduleStart inclusive to scheduleEnd exclusive (HH:mm), at the wall-clock time of the platform time zone. An end before the start is an overnight window that belongs to the weekday it starts on: 22:00–06:00 on Monday admits Monday 23:30 and Tuesday 05:59. The three fields are set together or not at all. A request outside the schedule is refused and charged nothing |
| Maximum usage | maxUsage | The capability may be invoked at most N times in total. The count is kept per grant, durably, by the service that enforces the capability (core-api's consumption ledger in PostgreSQL), and is never stored on the policy — one policy governing several grants gives each grant its own count. A use is charged immediately before the cryptographic operation runs, after every check that can refuse the request (the key's lifecycle, the security-strength floor, the key-protection limits, the timestamping policy, the payload size): a request one of those checks refuses is charged nothing, and an operation that then fails is still charged. The use that reaches N consumes the grant |
| Rate limit | rateLimitPerMinute | Maximum operations per grant per calendar minute (a fixed one-minute window, so approximate: a burst across a minute boundary can briefly reach twice the rate). Every admitted request counts in the minute, including one a later check refuses |
| Single use | revokeOnUse | The first operation the grant admits that reaches execution consumes it, whether or not the operation succeeds, and the grant can never be used again. The consumption is recorded durably at the enforcement point (core-api's ledger, in PostgreSQL) and then reflected on the grant, which is retired with its consumption. Single use authorizes one operation, not one message — to let a counterparty decrypt N messages once each, issue N single-use grants (or one per batch request) |
Key attributes:
| Attribute | Description |
|---|---|
| Constraint identifier | UUID |
| Version | The policy's current version: create writes version 1 and every edit writes the next one as an immutable record. Every version stays readable (listConstraintPolicyVersions, getConstraintPolicyVersion) with the number of live grants pinned to it, and two versions of one policy compare field by field (compareConstraintPolicyVersions) |
| Owning tenant | The tenant this policy belongs to |
| Name, description | Human-readable identity; the policy is referenced by name and is reusable |
| Time window | validFrom / validUntil (both optional) |
| Weekly schedule | scheduleWeekdays, scheduleStart, scheduleEnd (all three or none) |
| Usage cap | maxUsage (optional). The policy carries the cap only; the count against it is kept per grant at enforcement time and is not a member of the policy |
| Rate limit | rateLimitPerMinute (optional) |
| Single use | revokeOnUse (boolean, default false) — see the note above on what "single use" does and does not do to the grant |
| Schedule time zone | scheduleTimeZone, returned on every read: the platform time zone in force |
Five dimensions — and only five
The platform evaluates no batch or dataset scope, no network context requirement (source IP ranges, required headers, client certificates) and no behavioural auto-revocation on anomaly signals. The five dimensions in the table above are the complete set the control plane evaluates.
The platform time zone
A schedule is evaluated in one platform-wide time zone: the platform setting authorization.schedule.timezone, an IANA region identifier such as America/Bogota, or UTC, which is the default. Only a platform administrator changes it, and a fixed offset (+05:00) or an abbreviation (EST) is refused. A change applies to the next request without a restart. Schedules follow the wall clock across daylight-saving changes: 08:00 is 08:00 local before and after the change. Every Constraint Policy read returns the zone in force as scheduleTimeZone, so a tenant administrator sees it without access to platform settings.
The zone governs schedules only. A time window (validFrom, validUntil) is a pair of instants and means the same in every zone. If the enforcing service holds no usable zone value, every grant with a schedule is refused, and a grant without one is unaffected.
Versions, re-pin and usage counting
A policy is a template; a grant enforces a version of it. Every write of a Constraint Policy is an immutable version. A grant pins the version that is current when the grant is written, and enforces that version's limits until it is re-pinned. Editing a policy writes its next version; it does not move any grant by itself.
Re-pin applies a version to an issued grant. replaceCapabilityGrantPolicyVersion (scope admin.tenant.capability-grant.repin) pins a grant to any version of any Constraint Policy of the tenant: a later or an earlier version of its own policy, a version of another policy, or a first policy for a grant that had none. The grant's next request is evaluated against the new version. Every pin is appended to the grant's assignment history (listCapabilityGrantPolicyAssignments), which is never rewritten, and is recorded as one signed audit event naming both versions and the previous and new value of each limit that changed. getCapabilityGrantEffectiveRestrictions answers which version, limits and schedule were in force at a given instant. A consumed or revoked grant is never re-pinned, so a re-pin never revives a grant; a grant an Internal Crypto Use Case or the marketplace plan owns is re-pinned through its owner (409 capability-grant-repin-refused).
How uses are counted across a re-pin:
- A grant keeps the uses already counted against it, and the new version's
maxUsageapplies to that count. A grant with 8 counted uses re-pinned to a version withmaxUsage5 is refused on its next request. - Uses are counted only while the pinned version carries
maxUsageorrevokeOnUse. Uses made while no limit was pinned do not count toward a limit pinned later: a grant re-pinned from a version without either limit to a version withmaxUsage5 admits 5 further uses, however many operations it ran before.
The crypto audit row of every grant decision names the pinned pair (constraintPolicyId, constraintPolicyVersion).
Composability: the five dimensions compose by conjunction — all populated ones must be satisfied for the grant to authorize. A grant may be simultaneously time-windowed (valid within a stated period), scheduled (weekdays from 08:00 to 18:00), usage-capped (at most 50,000 invocations) and rate-limited (at most 500 per minute). A Constraint Policy is a tenant-level entity referenced by grants; the relationship is N:1, so one policy may govern several grants.
Policy
A Policy is a global or scoped rule that governs platform behavior. Policies exist at multiple levels:
- Deployment — regulatory region, allowed algorithms, FIPS posture
- Tenant — tenant-specific algorithm catalog, lifecycle defaults
- Exchange Context — rules specific to a business interaction
- Cryptographic Asset — rotation cadence, usage limits, exportability
- Capability — invocation rules, audit requirements
What policies define:
| Policy domain | Examples |
|---|---|
| Algorithm availability | Allowed algorithms and their security levels |
| Lifecycle rules | Rotation, expiration, archival cadence |
| Compliance constraints | FIPS, eIDAS 2, BSI, ANSSI, ETSI alignment |
| Crypto-agility rules | PQC enforcement, hybrid requirements, downgrade prevention |
| Security enforcement | Mandatory wrapping, key origin, attestation requirements |
Key attributes:
| Attribute | Description |
|---|---|
| Policy identifier | UUID |
| Scope | DEPLOYMENT, TENANT, EXCHANGE_CONTEXT, ASSET, CAPABILITY |
| Domain | ALGORITHM, LIFECYCLE, COMPLIANCE, AGILITY, SECURITY |
| Definition | Rule expression (algorithm catalog, allowed values, thresholds) |
| Inheritance | Resolution order (deployment baseline → tenant override → context override) |
Policy versus Constraint Policy: Policies govern platform behavior at architectural levels and apply to many grants. Constraint Policies govern individual grants. The two are complementary: a Policy may say "ML-DSA-87 is the required signature algorithm for the Treasury tenant"; a Constraint Policy may say "this specific grant is single-use and expires in 4 hours."
For the broader policy framework, see Policy-Driven Crypto-Agility.
Evidence entity
Audit Event
An Audit Event records every cryptographic operation and every authorization decision. Each event captures the full context of what happened.
Captured fields:
| Field | Content |
|---|---|
| Event identifier | UUID |
| Tenant | Organizational scope. A deployment identifier is not an audit field — the deployment is the installation the trail belongs to, not a value on the row |
| Application, Actor | Operational subject |
| Exchange Context | Business interaction context |
| Capability | Function invoked |
| Cryptographic Asset | Material involved |
| Constraints applied | Active conditions at decision time |
| Outcome | SUCCESS, FAILURE or DENIED (with reason). ALLOW / DENY is the vocabulary of a lifecycle-policy permission cell, a different decision on a different artifact, and is never an audit outcome |
| Timestamp | Decision time |
| Correlation identifier | Request trace ID for cross-service correlation |
| Source IP | Network origin |
| Request metadata | Additional contextual headers |
Integrity: audit events are HMAC-SHA256-signed at production time over the whole event envelope (metadata canonical fields plus the payload type's own canonical field list) and stored in an isolated audit data store. Tampering is detectable; events are immutable once produced.
Both allow and deny: the evidence layer captures the full decision context, not only successful operations. A denied invocation produces an audit record with the reason for denial, supporting forensic analysis and continuous monitoring.
What audit events enable: compliance reporting (DORA, PCI-DSS, SOX, GDPR), incident response, forensic reconstruction, continuous posture monitoring, and detection of anomalous patterns.
Control plane orchestration entities
The 12 entities defined above this section — every entity on this page except the one that follows — form the runtime authorization model. Core API runtime authorization resolves against Capability Grant and never references the orchestration layer described here.
The platform also exposes one control-plane orchestration entity that lives strictly above the runtime model and exists for administrative usability. The runtime model is unaffected by anything in this section.
Cryptographic Exchange
A Cryptographic Exchange is an admin-managed aggregate for one directed business relationship of the tenant: an origin and a destination, in one Exchange Context, with the Capability Grants that let the tenant's applications take part in it, under a single managed lifecycle.
Endpoints and direction. Each endpoint is an Application of the tenant or an external counterparty (a label, with an optional counterparty type); the two are never both external. The relationship is derived from them and enforced through the grants, never declared separately:
| Relationship | Origin → destination |
|---|---|
SELF | an application to itself |
INTERNAL | one application of the tenant to another |
OUTBOUND | an application of the tenant to an external counterparty |
INBOUND | an external counterparty to an application of the tenant |
Every crypto audit row an Exchange's grant authorizes records the relationship as direction and the external endpoint as counterparty.
Grants. An Exchange owns one or more Capability Grants, added with createExchangeGrant while it is not terminal and revoked with revokeExchangeGrant, which requires the operator's reason. Each grant belongs to the Exchange's Exchange Context, names an actor of the origin or destination application (422 exchange-grant-actor-not-participant otherwise), and names a Key ID or ordered key pairs, never a wildcard. The grants obey every rule of Capability Grant, the non-overlap rule included. A grant of an Exchange authorizes only while the Exchange is ACTIVE.
Lifecycle states:
| State | Meaning |
|---|---|
DRAFT | Being composed. Endpoints, context and grants can change; its grants authorize nothing |
READY | Validated. Still editable — an endpoint or context change returns it to DRAFT; its grants authorize nothing |
ACTIVE | Every live grant of the Exchange authorizes. Activation requires at least one live grant (409 exchange-not-activatable otherwise) and names no capability: the grants already say what is authorized |
SUSPENDED | Every grant of the Exchange is denied; the suspension's reason, time and author are recorded on the Exchange. Reversible: resume restores the same grants |
EXPIRED | Terminal — effective_until passed. Core-api already refuses every request at or after effective_until; the expiry job then records the state, its terminal cause and audit row |
CONSUMED | Terminal — its last live grant was used up (terminal cause CONSUMED). One grant used up retires alone; the Exchange stays ACTIVE while it keeps another. Fulfilled authority, not a revocation |
REVOKED | Terminal — manual revoke, the destruction of the key its last live grant named, the deletion of an endpoint Application, the close of its Exchange Context (terminal cause CONTEXT_CLOSED), or tenant closure |
The last live grant of an ACTIVE or SUSPENDED Exchange is not removed alone: revoke the Exchange to end it, or add another grant first. A terminal Exchange (EXPIRED, CONSUMED, REVOKED) cannot be suspended, resumed, edited or re-entered, accepts no grant change, and every live grant it owned is retired with it; clone it to compose a new one.
Cascade from
application.deleted(end-to-end). When an Application is terminally deleted, each Cryptographic Exchange it is an endpoint of transitions toREVOKEDand is soft-deleted as one step of the application's irreversible revoke cascade — alongside revoking the application's Internal Crypto Use Cases and Capability Grants, disabling its Cryptographic Actors (including the non-deletable Default Actor), and archiving its non-shared keys (material retained, never destroyed). Exchange Contexts belong to the tenant, so the deletion closes none. The cascade is terminal and best-effort; it never refuses on the presence of active dependents. See Application for the full ordered cascade.
Key attributes:
| Attribute | Description |
|---|---|
| Exchange identifier | UUID, stable for the lifetime of the aggregate |
| Owning tenant | The tenant that owns this Exchange |
| Name | Display name (e.g., VISA Embossing 2026-04-26) |
| Business purpose | Free-text purpose of the relationship |
| Origin, destination | The two endpoints, each an Application or an external counterparty |
| Relationship | SELF, INTERNAL, OUTBOUND or INBOUND, derived from the endpoints |
| Exchange Context | The governance scope every grant of the Exchange belongs to |
| Grants | The Capability Grants the Exchange owns, each with its actor, capabilities, Key ID or ordered pairs, and pinned policy version |
| Status | Lifecycle state (table above) |
| Owner principal | The admin who owns this Exchange |
| Effective from / until | Validity window (optional), half-open: usable from effective_from inclusive until effective_until exclusive. Sent as an RFC 3339 instant with an offset (a value without one is 400), stored and returned in UTC, and enforced by core-api on every request |
| Suspension metadata | suspended_at, suspended_by, suspended_reason while SUSPENDED, cleared on resume |
| Revocation metadata | revoked_at, revoked_by, revoked_reason and the terminal cause, populated on terminal transitions (revoked_by is system:scheduler for an expiry and system:consumption for a consumption) |
| Cloned from exchange id | Provenance pointer for clones |
| Tags | Free-form tag array |
| Notes | Free-text operator notes |
| Version | JPA optimistic-locking discriminator (carry into PATCH/transition request bodies) |
Composition vs reference (key invariant):
- Composes (1:N, owned): the Capability Grants. A grant has exactly one owner, so two Exchanges never share a grant.
- References (N:1): the endpoint Applications, the Exchange Context and the keys its grants name. An actor can hold grants in many Exchanges, as long as no two of its grants overlap.
Why this entity exists: the orchestration layer collapses the admin's mental model of a business relationship into one object: who is at each end, in which direction, and what each participating actor may do. Activate / Suspend / Resume / Revoke act on the Exchange and propagate atomically to every grant it owns. The runtime authorization path (Core API) still resolves against the grant, whose signed projection carries the owning Exchange's window and relationship.
API: the Cryptographic Exchange is exposed by ankasecure-admin-api under /api/v3/admin/exchanges. See Exchange Management for the full surface (list, detail, create, PATCH, validate, activate, suspend, resume, revoke, clone), including the mandatory operator justification that suspend and revoke require.
Quick reference table
| Entity | Layer | Cardinality |
|---|---|---|
| Deployment | Organizational | One per installation |
| Tenant | Organizational | Many per deployment |
| Application | Organizational | Many per tenant |
| Cryptographic Actor | Operational identity | Many per application |
| Credential | Operational identity | Many per actor (rotation) |
| Exchange Context | Governed exchange | Many per tenant |
| Cryptographic Asset | Cryptographic resource | Many per tenant |
| Capability | Cryptographic resource | Standard set, platform-defined |
| Capability Grant | Control | Many per actor |
| Constraint Policy | Control | Tenant-level and reusable; referenced by many grants (N:1) |
| Policy | Control | Scoped at multiple levels |
| Audit Event | Evidence | One per operation/decision |
| Cryptographic Exchange | Control plane orchestration | Many per tenant; owns 1..N Grants; references its endpoint Applications and its Context N:1 |
Next steps
- For relationships between these entities and the runtime decision flow, see Relationships and runtime flow.
- For worked examples that show these entities composed in real scenarios, see Reference scenarios.
- For the broader architectural context, return to Deployment organization model — Overview.