@axtary/actionpass 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -6
- package/dist/caep.d.ts +89 -0
- package/dist/caep.d.ts.map +1 -0
- package/dist/caep.js +258 -0
- package/dist/caep.js.map +1 -0
- package/dist/dev-key.d.ts +50 -0
- package/dist/dev-key.d.ts.map +1 -0
- package/dist/dev-key.js +103 -0
- package/dist/dev-key.js.map +1 -0
- package/dist/explain.d.ts +12 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +147 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts +1007 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1709 -62
- package/dist/index.js.map +1 -1
- package/dist/postgres.d.ts +21 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +112 -0
- package/dist/postgres.js.map +1 -0
- package/dist/provenance.d.ts +59 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +67 -0
- package/dist/provenance.js.map +1 -0
- package/dist/status-list.d.ts +158 -0
- package/dist/status-list.d.ts.map +1 -0
- package/dist/status-list.js +447 -0
- package/dist/status-list.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @axtary/actionpass
|
|
2
2
|
|
|
3
|
-
Scoped, signed ActionPass artifacts for runtime-governed AI agent actions.
|
|
3
|
+
Scoped, signed and proof-of-possession-bound ActionPass artifacts for runtime-governed AI agent actions.
|
|
4
4
|
|
|
5
5
|
Early `0.x` release: the runtime path is real and tested, but the API is not stable yet and may change between minor versions.
|
|
6
6
|
|
|
@@ -8,17 +8,54 @@ Early `0.x` release: the runtime path is real and tested, but the API is not sta
|
|
|
8
8
|
npm install @axtary/actionpass
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
The base format lives in `spec/actionpass-v0.md`; the sender-constrained
|
|
12
|
+
`cnf`/DPoP and delegation profile is `spec/actionpass-v1.md`; authenticated
|
|
13
|
+
status distribution is `spec/actionpass-v2.md`.
|
|
14
|
+
|
|
11
15
|
## What It Does
|
|
12
16
|
|
|
13
17
|
- Validates normalized agent actions at runtime.
|
|
14
18
|
- Produces canonical SHA-256 payload hashes.
|
|
15
19
|
- Produces payload-bound approval artifacts for exact human or policy override approvals.
|
|
16
20
|
- Issues signed ActionPass JWT/JWS artifacts for allowed actions.
|
|
17
|
-
-
|
|
21
|
+
- Issues ActionPass v1 bound to a holder key with RFC 7800 `cnf.jkt`.
|
|
22
|
+
- Creates and verifies RFC 9449 DPoP proofs with method/URI/token binding,
|
|
23
|
+
nonce, clock-window, and replay enforcement.
|
|
24
|
+
- Backs replay enforcement with a shared `DpopReplayStore`: an in-process
|
|
25
|
+
`InMemoryDpopReplayStore` and a durable, lock-serialized `FileDpopReplayStore`
|
|
26
|
+
(`0600`) that keeps a captured proof rejected across a verifier restart within
|
|
27
|
+
its window and compacts itself to the in-flight proof window. Single-host
|
|
28
|
+
state, not cross-host distribution.
|
|
29
|
+
- Exchanges a holder-authorized v1 pass for an attenuated child pass, walks
|
|
30
|
+
root-to-leaf delegation chains, enforces remaining depth, and binds the child
|
|
31
|
+
to a distinct sub-agent key.
|
|
32
|
+
- Applies one revocation source across the full delegation chain, so revoking a
|
|
33
|
+
root or intermediate parent invalidates every downstream child without
|
|
34
|
+
enumerating descendants.
|
|
35
|
+
- Binds authority-owned budget reservation cost/limit/state into the pass so a
|
|
36
|
+
presenter cannot alter metering fields.
|
|
37
|
+
- Produces durable local revocation records and rejects revoked passes during
|
|
38
|
+
verification; a revocation-source error also fails closed.
|
|
18
39
|
- Verifies passes against a keyring by `kid` so rotated keys can coexist.
|
|
19
40
|
- Persists local public verification keys and revocations in a JSON trust store.
|
|
41
|
+
- Issues and verifies ActionPass v2 with a signed Token Status List reference,
|
|
42
|
+
freshness-bounded caching, and fail-closed unavailable/stale/invalid status.
|
|
43
|
+
- Uses the IESG-approved Token Status List revision-21 wire format through the
|
|
44
|
+
pinned, stricter `axtary.status-list.v1` profile, so RFC number assignment is
|
|
45
|
+
not a runtime dependency.
|
|
46
|
+
- Validates `axtary.provenance.v0` field/source bindings and binds their
|
|
47
|
+
canonical hash into ActionPasses and ledger records.
|
|
48
|
+
- Persists the issuer ES256 keyring, publishes public JWKS by `kid`, and rotates
|
|
49
|
+
with bounded retired-key overlap.
|
|
50
|
+
- Verifies generic final SSF/CAEP `session-revoked` SETs and maps their explicit
|
|
51
|
+
subjects to live delegation roots without inventing provider support.
|
|
20
52
|
- Verifies that a signed pass and any embedded approval evidence still match the exact action payload.
|
|
21
|
-
- Records ledger entries with hashable decision evidence
|
|
53
|
+
- Records ledger entries with hashable decision evidence and parent-to-child
|
|
54
|
+
delegation edges.
|
|
55
|
+
- Owns the provider-neutral native-connector governance descriptors used to
|
|
56
|
+
derive GitHub/Jira/Linear/Postgres/Google Drive capability metadata, normalized evidence dispatch, config
|
|
57
|
+
defaults, and doctor scope/smoke metadata without importing secret-bearing
|
|
58
|
+
adapter runtimes.
|
|
22
59
|
|
|
23
60
|
## Current Status
|
|
24
61
|
|
|
@@ -27,13 +64,44 @@ npm install @axtary/actionpass
|
|
|
27
64
|
Before production use, Axtary still needs:
|
|
28
65
|
|
|
29
66
|
- Stable schema versioning.
|
|
30
|
-
- External signing-key management.
|
|
67
|
+
- External/HSM signing-key management.
|
|
31
68
|
- Hosted approval queue integration.
|
|
32
69
|
- External security review.
|
|
33
70
|
|
|
34
71
|
The package builds to `dist/` and publishes JavaScript plus TypeScript declarations.
|
|
35
72
|
|
|
36
|
-
##
|
|
73
|
+
## SDK facade
|
|
74
|
+
|
|
75
|
+
For most callers the `axtary` facade is the simplest entry point: five verbs —
|
|
76
|
+
`authorize`, `verify`, `record`, `revoke`, `explain` — over a flat request
|
|
77
|
+
shape. It delegates to the lower-level functions below and adds no new
|
|
78
|
+
authorization logic.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { axtary } from "@axtary/actionpass";
|
|
82
|
+
|
|
83
|
+
const decision = await axtary.authorize({
|
|
84
|
+
agent: "codex-prod",
|
|
85
|
+
human: "user@company.com",
|
|
86
|
+
intent: "Open a PR for Linear issue AXT-418",
|
|
87
|
+
tool: "github.pull_requests.create",
|
|
88
|
+
resource: "repo:company/web-app",
|
|
89
|
+
payload,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (decision.status === "allow") {
|
|
93
|
+
await github.createPullRequest(payload);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
With no signing key configured, the facade signs with a **persistent local dev
|
|
98
|
+
key** (`AXTARY_DEV_SIGNING_KEY`, else a `0600` keyring file under `.axtary/`), so
|
|
99
|
+
quickstart passes verify across restarts with zero key code. Call
|
|
100
|
+
`devKeypair()` to obtain that keypair explicitly, or
|
|
101
|
+
`createAxtary({ issuer, signingKey, verificationKey })` to use your own issuer
|
|
102
|
+
key in production. See the [SDK guide](https://docs.axtary.com/sdk).
|
|
103
|
+
|
|
104
|
+
## Quickstart (low-level functions)
|
|
37
105
|
|
|
38
106
|
This example runs as-is with Node 20+:
|
|
39
107
|
|
|
@@ -86,10 +154,24 @@ ActionPass is designed to fail closed:
|
|
|
86
154
|
- Denied and step-up actions do not receive passes.
|
|
87
155
|
- Verification rejects expired tokens.
|
|
88
156
|
- Verification rejects revoked pass IDs.
|
|
157
|
+
- Delegation verification checks every root-to-leaf member against the same
|
|
158
|
+
revocation source; an ancestor revoke cascades to all descendants.
|
|
159
|
+
- Verification rejects when a configured revocation source cannot be queried;
|
|
160
|
+
it never treats source failure as an empty set.
|
|
89
161
|
- Verification rejects payload hash mismatches.
|
|
90
162
|
- Pass issuance rejects approval artifacts that were created for a different action or payload.
|
|
91
163
|
- Keyring verification fails closed when the JWT `kid` is unknown.
|
|
92
|
-
- The local trust store
|
|
164
|
+
- The local trust store is atomically written with mode `0600`. It persists
|
|
165
|
+
public verification JWKs and local revocation records only; signing keys
|
|
166
|
+
should remain in KMS, env-managed dev secrets, or another controlled key
|
|
167
|
+
custodian.
|
|
168
|
+
- Local revocation records still rely on the trusted filesystem boundary.
|
|
169
|
+
ActionPass v2 additionally publishes signed freshness-bounded status evidence
|
|
170
|
+
for independent/multi-process verification.
|
|
93
171
|
- Verification binds agent, human owner, runtime, task, tool, resource, and payload hash.
|
|
172
|
+
- V1 verification additionally requires the holder key, fresh proof target,
|
|
173
|
+
exact ActionPass hash, and one-time proof `jti`.
|
|
174
|
+
- V2 verification additionally requires authenticated fresh status evidence;
|
|
175
|
+
status-source failure or stale evidence blocks execution.
|
|
94
176
|
|
|
95
177
|
Signing currently defaults to `ES256`.
|
package/dist/caep.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type CryptoKey, type JWK, type KeyObject } from "jose";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export declare const SSF_SET_TYP = "secevent+jwt";
|
|
4
|
+
export declare const CAEP_SESSION_REVOKED_EVENT = "https://schemas.openid.net/secevent/caep/event-type/session-revoked";
|
|
5
|
+
export declare const CAEP_MAPPING_STORE_VERSION = "axtary.caep_mapping_store.v0";
|
|
6
|
+
export declare const SSF_REPLAY_STORE_VERSION = "axtary.ssf_replay_store.v0";
|
|
7
|
+
export declare const SsfSubjectIdentifierSchema: z.ZodObject<{
|
|
8
|
+
format: z.ZodString;
|
|
9
|
+
}, z.core.$catchall<z.ZodType<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown, z.core.$ZodTypeInternals<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown>>>>;
|
|
10
|
+
export type SsfSubjectIdentifier = z.infer<typeof SsfSubjectIdentifierSchema>;
|
|
11
|
+
export declare const CaepSessionRevokedClaimsSchema: z.ZodObject<{
|
|
12
|
+
iss: z.ZodString;
|
|
13
|
+
aud: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
14
|
+
iat: z.ZodNumber;
|
|
15
|
+
jti: z.ZodString;
|
|
16
|
+
txn: z.ZodOptional<z.ZodString>;
|
|
17
|
+
sub_id: z.ZodObject<{
|
|
18
|
+
format: z.ZodString;
|
|
19
|
+
}, z.core.$catchall<z.ZodType<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown, z.core.$ZodTypeInternals<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown>>>>;
|
|
20
|
+
events: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodType<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown, z.core.$ZodTypeInternals<string | number | boolean | Record<string, unknown> | unknown[] | null, unknown>>>>;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type CaepSessionRevokedClaims = z.infer<typeof CaepSessionRevokedClaimsSchema>;
|
|
23
|
+
export declare const CaepMappingSchema: z.ZodObject<{
|
|
24
|
+
transmitterIssuer: z.ZodString;
|
|
25
|
+
subjectKey: z.ZodString;
|
|
26
|
+
rootPassIds: z.ZodArray<z.ZodString>;
|
|
27
|
+
createdAt: z.ZodString;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
export type CaepMapping = z.infer<typeof CaepMappingSchema>;
|
|
30
|
+
export declare const CaepMappingStoreSchema: z.ZodObject<{
|
|
31
|
+
schemaVersion: z.ZodLiteral<"axtary.caep_mapping_store.v0">;
|
|
32
|
+
mappings: z.ZodArray<z.ZodObject<{
|
|
33
|
+
transmitterIssuer: z.ZodString;
|
|
34
|
+
subjectKey: z.ZodString;
|
|
35
|
+
rootPassIds: z.ZodArray<z.ZodString>;
|
|
36
|
+
createdAt: z.ZodString;
|
|
37
|
+
}, z.core.$strip>>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export type CaepMappingStoreData = z.infer<typeof CaepMappingStoreSchema>;
|
|
40
|
+
export declare function ssfSubjectKey(subjectInput: SsfSubjectIdentifier): string;
|
|
41
|
+
export declare function verifyCaepSessionRevoked(input: {
|
|
42
|
+
token: string;
|
|
43
|
+
issuer: string;
|
|
44
|
+
audience: string;
|
|
45
|
+
verificationKeys?: Readonly<Record<string, JWK | KeyObject | CryptoKey | Uint8Array>> | ((kid: string | undefined) => JWK | KeyObject | CryptoKey | Uint8Array | undefined | Promise<JWK | KeyObject | CryptoKey | Uint8Array | undefined>);
|
|
46
|
+
jwksUri?: string;
|
|
47
|
+
currentDate?: Date;
|
|
48
|
+
maxAgeSeconds?: number;
|
|
49
|
+
}): Promise<CaepSessionRevokedClaims>;
|
|
50
|
+
export declare class LocalCaepMappingStore {
|
|
51
|
+
readonly filePath: string;
|
|
52
|
+
constructor(filePath: string);
|
|
53
|
+
read(): Promise<CaepMappingStoreData>;
|
|
54
|
+
put(input: {
|
|
55
|
+
transmitterIssuer: string;
|
|
56
|
+
subject: SsfSubjectIdentifier;
|
|
57
|
+
rootPassIds: string[];
|
|
58
|
+
createdAt?: Date;
|
|
59
|
+
}): Promise<CaepMapping>;
|
|
60
|
+
rootsFor(transmitterIssuer: string, subject: SsfSubjectIdentifier): Promise<string[]>;
|
|
61
|
+
}
|
|
62
|
+
export declare class LocalSsfReplayStore {
|
|
63
|
+
readonly filePath: string;
|
|
64
|
+
constructor(filePath: string);
|
|
65
|
+
consume(issuer: string, eventId: string, now?: Date, retentionSeconds?: number): Promise<boolean>;
|
|
66
|
+
processOnce<T>(issuer: string, eventId: string, operation: () => Promise<T>, now?: Date, retentionSeconds?: number): Promise<{
|
|
67
|
+
processed: true;
|
|
68
|
+
result: T;
|
|
69
|
+
} | {
|
|
70
|
+
processed: false;
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
export declare function processCaepSessionRevoked(input: {
|
|
74
|
+
token: string;
|
|
75
|
+
issuer: string;
|
|
76
|
+
audience: string;
|
|
77
|
+
verificationKeys?: Readonly<Record<string, JWK | KeyObject | CryptoKey | Uint8Array>> | ((kid: string | undefined) => JWK | KeyObject | CryptoKey | Uint8Array | undefined | Promise<JWK | KeyObject | CryptoKey | Uint8Array | undefined>);
|
|
78
|
+
jwksUri?: string;
|
|
79
|
+
mappingStore: LocalCaepMappingStore;
|
|
80
|
+
replayStore: LocalSsfReplayStore;
|
|
81
|
+
revoke: (rootPassId: string, claims: CaepSessionRevokedClaims) => Promise<void>;
|
|
82
|
+
currentDate?: Date;
|
|
83
|
+
}): Promise<{
|
|
84
|
+
eventId: string;
|
|
85
|
+
subjectKey: string;
|
|
86
|
+
rootPassIds: string[];
|
|
87
|
+
status: "applied" | "unmapped" | "replayed";
|
|
88
|
+
}>;
|
|
89
|
+
//# sourceMappingURL=caep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caep.d.ts","sourceRoot":"","sources":["../src/caep.ts"],"names":[],"mappings":"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"}
|
package/dist/caep.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { createRemoteJWKSet, decodeProtectedHeader, jwtVerify, } from "jose";
|
|
5
|
+
import { lock } from "proper-lockfile";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export const SSF_SET_TYP = "secevent+jwt";
|
|
8
|
+
export const CAEP_SESSION_REVOKED_EVENT = "https://schemas.openid.net/secevent/caep/event-type/session-revoked";
|
|
9
|
+
export const CAEP_MAPPING_STORE_VERSION = "axtary.caep_mapping_store.v0";
|
|
10
|
+
export const SSF_REPLAY_STORE_VERSION = "axtary.ssf_replay_store.v0";
|
|
11
|
+
const JsonValueSchema = z.lazy(() => z.union([
|
|
12
|
+
z.string(),
|
|
13
|
+
z.number(),
|
|
14
|
+
z.boolean(),
|
|
15
|
+
z.null(),
|
|
16
|
+
z.array(JsonValueSchema),
|
|
17
|
+
z.record(z.string(), JsonValueSchema),
|
|
18
|
+
]));
|
|
19
|
+
export const SsfSubjectIdentifierSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
format: z.string().min(1),
|
|
22
|
+
})
|
|
23
|
+
.catchall(JsonValueSchema);
|
|
24
|
+
export const CaepSessionRevokedClaimsSchema = z.object({
|
|
25
|
+
iss: z.string().url(),
|
|
26
|
+
aud: z.union([z.string().min(1), z.array(z.string().min(1))]),
|
|
27
|
+
iat: z.number().int().positive(),
|
|
28
|
+
jti: z.string().min(1),
|
|
29
|
+
txn: z.string().min(1).optional(),
|
|
30
|
+
sub_id: SsfSubjectIdentifierSchema,
|
|
31
|
+
events: z.record(z.string(), z.record(z.string(), JsonValueSchema)),
|
|
32
|
+
});
|
|
33
|
+
export const CaepMappingSchema = z.object({
|
|
34
|
+
transmitterIssuer: z.string().url(),
|
|
35
|
+
subjectKey: z.string().min(1),
|
|
36
|
+
rootPassIds: z.array(z.string().min(1)).min(1),
|
|
37
|
+
createdAt: z.string().datetime(),
|
|
38
|
+
});
|
|
39
|
+
export const CaepMappingStoreSchema = z.object({
|
|
40
|
+
schemaVersion: z.literal(CAEP_MAPPING_STORE_VERSION),
|
|
41
|
+
mappings: z.array(CaepMappingSchema),
|
|
42
|
+
});
|
|
43
|
+
const SsfReplayEntrySchema = z.object({
|
|
44
|
+
issuer: z.string().url(),
|
|
45
|
+
eventId: z.string().min(1),
|
|
46
|
+
expiresAt: z.string().datetime(),
|
|
47
|
+
});
|
|
48
|
+
const SsfReplayStoreSchema = z.object({
|
|
49
|
+
schemaVersion: z.literal(SSF_REPLAY_STORE_VERSION),
|
|
50
|
+
entries: z.array(SsfReplayEntrySchema),
|
|
51
|
+
});
|
|
52
|
+
export function ssfSubjectKey(subjectInput) {
|
|
53
|
+
return stableStringify(SsfSubjectIdentifierSchema.parse(subjectInput));
|
|
54
|
+
}
|
|
55
|
+
export async function verifyCaepSessionRevoked(input) {
|
|
56
|
+
const header = decodeProtectedHeader(input.token);
|
|
57
|
+
if (header.typ !== SSF_SET_TYP) {
|
|
58
|
+
throw new Error("ssf_set_type_invalid");
|
|
59
|
+
}
|
|
60
|
+
const localKey = input.jwksUri
|
|
61
|
+
? undefined
|
|
62
|
+
: await resolveKey(input.verificationKeys, header.kid);
|
|
63
|
+
if (!input.jwksUri && !localKey) {
|
|
64
|
+
throw new Error(header.kid
|
|
65
|
+
? `ssf_verification_key_not_found:${header.kid}`
|
|
66
|
+
: "ssf_verification_key_id_required");
|
|
67
|
+
}
|
|
68
|
+
const currentDate = input.currentDate ?? new Date();
|
|
69
|
+
const verifyOptions = {
|
|
70
|
+
issuer: input.issuer,
|
|
71
|
+
audience: input.audience,
|
|
72
|
+
algorithms: ["ES256"],
|
|
73
|
+
currentDate,
|
|
74
|
+
};
|
|
75
|
+
const { payload } = input.jwksUri
|
|
76
|
+
? await jwtVerify(input.token, createRemoteJWKSet(new URL(input.jwksUri)), verifyOptions)
|
|
77
|
+
: await jwtVerify(input.token, localKey, verifyOptions);
|
|
78
|
+
const claims = CaepSessionRevokedClaimsSchema.parse(payload);
|
|
79
|
+
const ageSeconds = Math.floor(currentDate.getTime() / 1000) - claims.iat;
|
|
80
|
+
if (ageSeconds < -5 || ageSeconds > (input.maxAgeSeconds ?? 300)) {
|
|
81
|
+
throw new Error("ssf_set_outside_accepted_window");
|
|
82
|
+
}
|
|
83
|
+
const eventKeys = Object.keys(claims.events);
|
|
84
|
+
if (eventKeys.length !== 1 ||
|
|
85
|
+
eventKeys[0] !== CAEP_SESSION_REVOKED_EVENT) {
|
|
86
|
+
throw new Error("caep_event_type_unsupported");
|
|
87
|
+
}
|
|
88
|
+
return claims;
|
|
89
|
+
}
|
|
90
|
+
export class LocalCaepMappingStore {
|
|
91
|
+
filePath;
|
|
92
|
+
constructor(filePath) {
|
|
93
|
+
this.filePath = filePath;
|
|
94
|
+
}
|
|
95
|
+
async read() {
|
|
96
|
+
try {
|
|
97
|
+
return CaepMappingStoreSchema.parse(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (isEnoent(error)) {
|
|
101
|
+
return { schemaVersion: CAEP_MAPPING_STORE_VERSION, mappings: [] };
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async put(input) {
|
|
107
|
+
const mapping = CaepMappingSchema.parse({
|
|
108
|
+
transmitterIssuer: input.transmitterIssuer,
|
|
109
|
+
subjectKey: ssfSubjectKey(input.subject),
|
|
110
|
+
rootPassIds: [...new Set(input.rootPassIds)].sort(),
|
|
111
|
+
createdAt: (input.createdAt ?? new Date()).toISOString(),
|
|
112
|
+
});
|
|
113
|
+
await withFileLock(this.filePath, async () => {
|
|
114
|
+
const data = await this.read();
|
|
115
|
+
await writePrivateJson(this.filePath, {
|
|
116
|
+
...data,
|
|
117
|
+
mappings: [
|
|
118
|
+
...data.mappings.filter((entry) => !(entry.transmitterIssuer === mapping.transmitterIssuer &&
|
|
119
|
+
entry.subjectKey === mapping.subjectKey)),
|
|
120
|
+
mapping,
|
|
121
|
+
].sort((left, right) => `${left.transmitterIssuer}:${left.subjectKey}`.localeCompare(`${right.transmitterIssuer}:${right.subjectKey}`)),
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
return mapping;
|
|
125
|
+
}
|
|
126
|
+
async rootsFor(transmitterIssuer, subject) {
|
|
127
|
+
const key = ssfSubjectKey(subject);
|
|
128
|
+
return ((await this.read()).mappings.find((entry) => entry.transmitterIssuer === transmitterIssuer &&
|
|
129
|
+
entry.subjectKey === key)?.rootPassIds ?? []);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export class LocalSsfReplayStore {
|
|
133
|
+
filePath;
|
|
134
|
+
constructor(filePath) {
|
|
135
|
+
this.filePath = filePath;
|
|
136
|
+
}
|
|
137
|
+
async consume(issuer, eventId, now = new Date(), retentionSeconds = 86_400) {
|
|
138
|
+
return (await this.processOnce(issuer, eventId, async () => undefined, now, retentionSeconds)).processed;
|
|
139
|
+
}
|
|
140
|
+
async processOnce(issuer, eventId, operation, now = new Date(), retentionSeconds = 86_400) {
|
|
141
|
+
return withFileLock(this.filePath, async () => {
|
|
142
|
+
let data;
|
|
143
|
+
try {
|
|
144
|
+
data = SsfReplayStoreSchema.parse(JSON.parse(await readFile(this.filePath, "utf8")));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (!isEnoent(error))
|
|
148
|
+
throw error;
|
|
149
|
+
data = { schemaVersion: SSF_REPLAY_STORE_VERSION, entries: [] };
|
|
150
|
+
}
|
|
151
|
+
const live = data.entries.filter((entry) => Date.parse(entry.expiresAt) > now.getTime());
|
|
152
|
+
if (live.some((entry) => entry.issuer === issuer && entry.eventId === eventId)) {
|
|
153
|
+
return { processed: false };
|
|
154
|
+
}
|
|
155
|
+
const result = await operation();
|
|
156
|
+
live.push({
|
|
157
|
+
issuer,
|
|
158
|
+
eventId,
|
|
159
|
+
expiresAt: new Date(now.getTime() + retentionSeconds * 1000).toISOString(),
|
|
160
|
+
});
|
|
161
|
+
await writePrivateJson(this.filePath, {
|
|
162
|
+
schemaVersion: SSF_REPLAY_STORE_VERSION,
|
|
163
|
+
entries: live,
|
|
164
|
+
});
|
|
165
|
+
return { processed: true, result };
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export async function processCaepSessionRevoked(input) {
|
|
170
|
+
const claims = await verifyCaepSessionRevoked(input);
|
|
171
|
+
const processed = await input.replayStore.processOnce(claims.iss, claims.jti, async () => {
|
|
172
|
+
const rootPassIds = await input.mappingStore.rootsFor(claims.iss, claims.sub_id);
|
|
173
|
+
for (const rootPassId of rootPassIds) {
|
|
174
|
+
await input.revoke(rootPassId, claims);
|
|
175
|
+
}
|
|
176
|
+
return rootPassIds;
|
|
177
|
+
}, input.currentDate);
|
|
178
|
+
if (!processed.processed) {
|
|
179
|
+
return {
|
|
180
|
+
eventId: claims.jti,
|
|
181
|
+
subjectKey: ssfSubjectKey(claims.sub_id),
|
|
182
|
+
rootPassIds: [],
|
|
183
|
+
status: "replayed",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const rootPassIds = processed.result;
|
|
187
|
+
return {
|
|
188
|
+
eventId: claims.jti,
|
|
189
|
+
subjectKey: ssfSubjectKey(claims.sub_id),
|
|
190
|
+
rootPassIds,
|
|
191
|
+
status: rootPassIds.length > 0 ? "applied" : "unmapped",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function resolveKey(keys, kid) {
|
|
195
|
+
if (!keys)
|
|
196
|
+
return undefined;
|
|
197
|
+
return typeof keys === "function" ? keys(kid) : kid ? keys[kid] : undefined;
|
|
198
|
+
}
|
|
199
|
+
async function writePrivateJson(filePath, value) {
|
|
200
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
201
|
+
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
202
|
+
let renamed = false;
|
|
203
|
+
try {
|
|
204
|
+
const handle = await open(tmpPath, "wx", 0o600);
|
|
205
|
+
try {
|
|
206
|
+
await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
207
|
+
await handle.sync();
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
await handle.close();
|
|
211
|
+
}
|
|
212
|
+
await rename(tmpPath, filePath);
|
|
213
|
+
renamed = true;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
if (!renamed)
|
|
217
|
+
await rm(tmpPath, { force: true });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function withFileLock(filePath, operation) {
|
|
221
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
222
|
+
const release = await lock(filePath, {
|
|
223
|
+
realpath: false,
|
|
224
|
+
lockfilePath: `${filePath}.lock`,
|
|
225
|
+
stale: 10_000,
|
|
226
|
+
update: 5_000,
|
|
227
|
+
retries: {
|
|
228
|
+
retries: 100,
|
|
229
|
+
factor: 1.2,
|
|
230
|
+
minTimeout: 10,
|
|
231
|
+
maxTimeout: 100,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
return await operation();
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
await release();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function stableStringify(value) {
|
|
242
|
+
if (Array.isArray(value)) {
|
|
243
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
244
|
+
}
|
|
245
|
+
if (value && typeof value === "object") {
|
|
246
|
+
return `{${Object.entries(value)
|
|
247
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
248
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)
|
|
249
|
+
.join(",")}}`;
|
|
250
|
+
}
|
|
251
|
+
return JSON.stringify(value);
|
|
252
|
+
}
|
|
253
|
+
function isEnoent(error) {
|
|
254
|
+
return (error instanceof Error &&
|
|
255
|
+
"code" in error &&
|
|
256
|
+
error.code === "ENOENT");
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=caep.js.map
|
package/dist/caep.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caep.js","sourceRoot":"","sources":["../src/caep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type JWK, type KeyObject } from "jose";
|
|
2
|
+
/** Env var holding an ES256 private key (PEM, or base64-encoded PEM). */
|
|
3
|
+
export declare const AXTARY_DEV_SIGNING_KEY_ENV = "AXTARY_DEV_SIGNING_KEY";
|
|
4
|
+
/** Optional env var overriding the dev key id (`kid`). */
|
|
5
|
+
export declare const AXTARY_DEV_KEY_ID_ENV = "AXTARY_DEV_KEY_ID";
|
|
6
|
+
/** Env var pointing the local keyring at a directory other than ./.axtary. */
|
|
7
|
+
export declare const AXTARY_HOME_ENV = "AXTARY_HOME";
|
|
8
|
+
/** Filename of the file-backed dev keyring inside the resolved home dir. */
|
|
9
|
+
export declare const AXTARY_DEV_KEYRING_FILENAME = "sdk-dev-keyring.json";
|
|
10
|
+
/** Default `kid` for an env-backed dev key when none is supplied. */
|
|
11
|
+
export declare const AXTARY_DEFAULT_DEV_KEY_ID = "axtary-dev";
|
|
12
|
+
export type DevKeypairSource = "env" | "file";
|
|
13
|
+
export type DevKeypair = {
|
|
14
|
+
/** ES256 private key, ready to pass to createAxtary({ signingKey }). */
|
|
15
|
+
signingKey: KeyObject;
|
|
16
|
+
/** Matching public JWK, used by verify(). */
|
|
17
|
+
verificationKey: JWK;
|
|
18
|
+
/** Key id stamped into issued passes (`kid`). */
|
|
19
|
+
signingKeyId: string;
|
|
20
|
+
algorithm: "ES256";
|
|
21
|
+
/** Where the key came from: an env var or the local keyring file. */
|
|
22
|
+
source: DevKeypairSource;
|
|
23
|
+
/** Keyring file path when source === "file"; omitted for env-backed keys. */
|
|
24
|
+
path?: string;
|
|
25
|
+
};
|
|
26
|
+
export type DevKeypairOptions = {
|
|
27
|
+
/** Override the keyring file path (file-backed source). */
|
|
28
|
+
path?: string;
|
|
29
|
+
/** Environment to read; defaults to process.env. */
|
|
30
|
+
env?: Record<string, string | undefined>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the file-backed dev keyring path: an explicit override, else
|
|
34
|
+
* `$AXTARY_HOME/sdk-dev-keyring.json`, else project-local
|
|
35
|
+
* `.axtary/sdk-dev-keyring.json`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveDevKeyringPath(options?: DevKeypairOptions): string;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a stable, persistent ES256 dev keypair with zero manual JWK code.
|
|
40
|
+
* Resolution order: AXTARY_DEV_SIGNING_KEY (env-backed, shared across
|
|
41
|
+
* processes) → a local keyring file (default `.axtary/sdk-dev-keyring.json`,
|
|
42
|
+
* `0600`, gitignored). Both verify across process restarts. Pass the result
|
|
43
|
+
* straight to the SDK: `createAxtary(await devKeypair())`.
|
|
44
|
+
*
|
|
45
|
+
* This is a development convenience. For production use a real issuer key or a
|
|
46
|
+
* hosted issuer; the file-backed key never leaves the machine and the env-backed
|
|
47
|
+
* key is whatever the operator provisions.
|
|
48
|
+
*/
|
|
49
|
+
export declare function devKeypair(options?: DevKeypairOptions): Promise<DevKeypair>;
|
|
50
|
+
//# sourceMappingURL=dev-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-key.d.ts","sourceRoot":"","sources":["../src/dev-key.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAa,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,MAAM,CAAC;AAG3D,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AACnE,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AACzD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAClE,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,eAAe,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,CAAC;AAE9C,MAAM,MAAM,UAAU,GAAG;IACvB,wEAAwE;IACxE,UAAU,EAAE,SAAS,CAAC;IACtB,6CAA6C;IAC7C,eAAe,EAAE,GAAG,CAAC;IACrB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,MAAM,EAAE,gBAAgB,CAAC;IACzB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAU7E;AAgCD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,UAAU,CAAC,CAkBrB"}
|