Contract and use
Store interfaces require deployment-specific durable implementations for production use. The included in-memory stores are not distributed locks or exactly-once guarantees. Configure assertion enforcement explicitly.
Core and adapter responsibilities
| API | Supplies | Does not supply |
|---|---|---|
registerRoute / routeFor | Route metadata and shared path/method matching | Framework registration, user permissions or an HTTP listener |
verifyRequest | Disabled-route check and gateway verification against supplied route/body | Business validation, body-size limits or concrete resource authorization |
GatewayVerifier.verify | Configured assertion verification and expected-claim checks | Automatic trust enrolment or universal callback policy |
emitReceipt | Result/hash record in the configured store | Independent proof of an external effect |
idempotent | Sequential get/execute/put reuse | Atomic concurrency exclusion, changed-input binding or crash recovery |
syncAck / createSyncAck | Protocol-shaped acknowledgement | Applying/committing a database mutation |
cappRoute from /next | Verification before a framework handler and optional response receipt | HTTP router, user authentication or durable storage |
Use a literal path or additionally check actual request/resource binding in your adapter. verifyRequest() receives a registered route definition, not a URL-bearing Request; do not infer concrete resource authorization from a pattern match. It does not independently enforce the full methods array when called directly—use routeFor(actualPath, method) or framework dispatch and check the result.
Assertion modes and ownership
| Mode | Boundary |
|---|---|
| Missing/passive | Default enforcement is off. Decoded claims can be unverified; never use them as authenticated identity. |
| HMAC compatibility | Requires a backend-only secret. This path does not provide the same built-in replay claim as Ed25519 install-key verification. |
| Ed25519 install key | Requires an enrolled, non-revoked public key and matching identity; checks time bounds and claims a replay identity. |
Custom verifyToken callback | Your callback must perform signature, time and replay policy. The SDK checks expected fields and enforced verified, but does not run the built-in verifier pipeline again. |
The default built-in skew is 60 seconds and maximum Ed25519 assertion lifetime is 300 seconds. Configure deployment policy deliberately. Claims are not copied into a trusted user session just because the signature verifies. GatewayReplayStore.claim() must atomically accept one claim across the deployment; independent in-memory maps do not provide that property.
Use Headers for consistent header lookup at the boundary. Prefer the dedicated x-capp-gateway-assertion transport header rather than confusing a normal user bearer token with a cApp assertion. Do not log the signed token or private trust metadata.
Receipts and error mapping
CappPolicyError.statusCode is the TypeScript HTTP status field; Python uses status_code. The Next adapter maps policy failures to capp.policy-error.v1 before calling the handler. Other handler/storage exceptions remain application errors. Automatic receipts may buffer the returned response, so use explicit limits or application-owned receipt handling for streams and large bodies.
Do not call verification twice under one assertion: replay protection can reject the second verification. Pass the verified gateway/context into the operation and receipt. Receipt storage failure after the handler ran is a potential unknown result, not proof that repeating the handler is safe.
The declarations below are snapshots for reading. Import their named exports from the package; they are not a standalone file to paste without their referenced types.
CappRoutePolicy
export type CappRoutePolicy = {
capability: string;
group: string;
risk?: CappRisk;
auth?: string;
mode?: string;
browserCallable?: boolean;
receiptRequired?: boolean;
approval?: string;
replayProtection?: string;
coldBehavior?: string;
defaultEnabled?: boolean;
online?: boolean;
offline?: boolean;
offlineCollection?: string | null;
localHandler?: string | null;
sync?: string;
routeMode?: string;
metadata?: Record<string, unknown>;
};CappReceipt
export type CappReceipt = {
protocol: 'capp.receipt.v1';
receiptId: string;
serverId: string;
route: string;
method: string;
capability: string;
group: string;
risk: CappRisk;
result: string;
createdAt: string;
requestId?: string;
subject?: string;
bodySha256?: string;
responseSha256?: string;
gateway?: CappGatewayAssertion;
metadata?: Record<string, unknown>;
};ReceiptStore
export interface ReceiptStore {
put(receipt: CappReceipt): void | Promise<void>;
get(receiptId: string): CappReceipt | undefined | Promise<CappReceipt | undefined>;
listRecent(limit?: number): CappReceipt[] | Promise<CappReceipt[]>;
}IdempotencyStore
export interface IdempotencyStore {
get<T = unknown>(key: string): CappIdempotencyEntry<T> | undefined | Promise<CappIdempotencyEntry<T> | undefined>;
put<T = unknown>(entry: CappIdempotencyEntry<T>): void | Promise<void>;
}GatewayExpected
export type GatewayExpected = {
route?: string;
method?: string;
capability?: string;
audience?: string;
appId?: string;
siteId?: string;
capsuleHash?: string;
bindingId?: string;
bodySha256?: string;
subject?: string;
};CappInstallTrustRecord
export type CappInstallTrustRecord = {
protocol: 'capp.gateway-trust.v1';
keyId: string;
installId: string;
publicKeyJwk: JsonWebKey;
appId?: string;
siteId?: string;
revoked?: boolean;
metadata?: Record<string, unknown>;
};GatewayTrustStore
export interface GatewayTrustStore {
get(keyId: string, installId?: string): CappInstallTrustRecord | undefined | Promise<CappInstallTrustRecord | undefined>;
}GatewayReplayStore
export interface GatewayReplayStore {
claim(jti: string, expiresAt: number): boolean | Promise<boolean>;
}GatewayVerifierOptions
export type GatewayVerifierOptions = {
enforce?: boolean;
hmacSecret?: string | Uint8Array;
verifyToken?: GatewayVerificationCallback;
trustStore?: GatewayTrustStore;
replayStore?: GatewayReplayStore;
maxClockSkewSeconds?: number;
maxAssertionLifetimeSeconds?: number;
};CappServer
export declare class CappServer {
readonly serverId: string;
readonly origin: string;
readonly verifier: GatewayVerifier;
readonly receipts: ReceiptStore;
readonly idempotency: IdempotencyStore;
readonly metadata: Record<string, unknown>;
readonly gatewayExpected: Omit<GatewayExpected, 'route' | 'method' | 'capability' | 'bodySha256'>;
constructor(options: {
serverId: string;
origin?: string;
enforceGatewayAssertions?: boolean;
gatewayHmacSecret?: string | Uint8Array;
verifyGatewayAssertion?: GatewayVerificationCallback;
gatewayTrustStore?: GatewayTrustStore;
gatewayReplayStore?: GatewayReplayStore;
gatewayMaxClockSkewSeconds?: number;
gatewayMaxAssertionLifetimeSeconds?: number;
gatewayAudience?: string;
gatewayAppId?: string;
gatewaySiteId?: string;
gatewayCapsuleHash?: string;
gatewayBindingId?: string;
receiptStore?: ReceiptStore;
idempotencyStore?: IdempotencyStore;
metadata?: Record<string, unknown>;
});
idempotent<T>(key: string | undefined | null, execute: () => T | Promise<T>): Promise<{
duplicate: boolean;
value: T;
}>;
syncAck<T = unknown>(input: Omit<CappSyncAck<T>, 'protocol'>): CappSyncAck<T>;
registerRoute(path: string, methods: string[], policy: CappRoutePolicy, kind?: string, source?: string): CappRouteDefinition;
routeFor(path: string, method: string): CappRouteDefinition | undefined;
verifyRequest(route: CappRouteDefinition, method: string, headers: Headers | Record<string, string>, body?: string | Uint8Array | ArrayBuffer | null): Promise<CappGatewayAssertion>;
emitReceipt(input: {
route: CappRouteDefinition;
method: string;
result: string;
requestId?: string;
subject?: string;
body?: string | Uint8Array | ArrayBuffer | null;
responseBody?: string | Uint8Array | ArrayBuffer | null;
gateway?: CappGatewayAssertion;
metadata?: Record<string, unknown>;
}): Promise<CappReceipt>;
serverContract(): Record<string, unknown>;
}CappRouteDefinition
export type CappRouteDefinition = {
path: string;
methods: string[];
kind?: string;
source?: string;
policy: CappRoutePolicy;
};CappGatewayAssertion
export type CappGatewayAssertion = {
protocol?: string;
audience?: string;
appId?: string;
installId?: string;
keyId?: string;
capsuleHash?: string;
siteId?: string;
bindingId?: string;
capability?: string;
route?: string;
method?: string;
bodySha256?: string;
requestId?: string;
jti?: string;
subject?: string;
issuedAt?: number;
notBefore?: number;
expiresAt?: number;
verified: boolean;
verificationMode: string;
rawClaims?: Record<string, unknown>;
};CappIdempotencyEntry
export type CappIdempotencyEntry<T = unknown> = {
key: string;
createdAt: string;
value: T;
};GatewayVerifier
export declare class GatewayVerifier {
readonly options: GatewayVerifierOptions;
constructor(options?: GatewayVerifierOptions);
signClaims(claims: Record<string, unknown>): Promise<string>;
verify(token: string | undefined | null, expected?: GatewayExpected): Promise<CappGatewayAssertion>;
}createSyncAck
export declare function createSyncAck<T = unknown>(input: Omit<CappSyncAck<T>, 'protocol'>): CappSyncAck<T>;cappRoute
export declare function cappRoute(server: CappServer, path: string, methods: string[], policy: CappRoutePolicy, handler: NextHandler): NextHandler;cappShowcaseDemo
export declare function cappShowcaseDemo(snapshot: CappShowcaseDemoSnapshot | (() => CappShowcaseDemoSnapshot | Promise<CappShowcaseDemoSnapshot>)): NextHandler;