@axtary/actionpass 0.0.1 → 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/LICENSE +202 -0
- package/README.md +134 -21
- 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 +1670 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2851 -0
- package/dist/index.js.map +1 -0
- 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 +18 -6
- package/src/index.ts +0 -552
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"}
|
package/dist/dev-key.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Dev keypair helper (PRD §8.2 developer experience; mega-plan M8.2)
|
|
3
|
+
//
|
|
4
|
+
// The SDK facade signs ActionPasses with an issuer key. In production a
|
|
5
|
+
// developer supplies their own key (or a hosted issuer); for local development
|
|
6
|
+
// we want zero manual JWK plumbing while still producing passes that verify
|
|
7
|
+
// across process restarts (an ephemeral per-process key cannot).
|
|
8
|
+
//
|
|
9
|
+
// `devKeypair()` resolves a stable ES256 keypair with no key code:
|
|
10
|
+
// 1. AXTARY_DEV_SIGNING_KEY — env-backed, shared across processes/CI/containers
|
|
11
|
+
// 2. a local keyring file — default .axtary/sdk-dev-keyring.json (0600, gitignored)
|
|
12
|
+
// Both survive restarts. Spread the result directly into createAxtary():
|
|
13
|
+
// const sdk = createAxtary(await devKeypair());
|
|
14
|
+
//
|
|
15
|
+
// This adds no new authorization or token/ledger/policy shape; it only produces
|
|
16
|
+
// the same { signingKey, verificationKey } the facade already accepts, reusing
|
|
17
|
+
// the existing persistent issuer-keyring primitive (status-list.ts).
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
import { createPrivateKey, createPublicKey } from "node:crypto";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { exportJWK } from "jose";
|
|
22
|
+
import { issuerSigningKey, loadOrCreateIssuerKeyring } from "./status-list.js";
|
|
23
|
+
/** Env var holding an ES256 private key (PEM, or base64-encoded PEM). */
|
|
24
|
+
export const AXTARY_DEV_SIGNING_KEY_ENV = "AXTARY_DEV_SIGNING_KEY";
|
|
25
|
+
/** Optional env var overriding the dev key id (`kid`). */
|
|
26
|
+
export const AXTARY_DEV_KEY_ID_ENV = "AXTARY_DEV_KEY_ID";
|
|
27
|
+
/** Env var pointing the local keyring at a directory other than ./.axtary. */
|
|
28
|
+
export const AXTARY_HOME_ENV = "AXTARY_HOME";
|
|
29
|
+
/** Filename of the file-backed dev keyring inside the resolved home dir. */
|
|
30
|
+
export const AXTARY_DEV_KEYRING_FILENAME = "sdk-dev-keyring.json";
|
|
31
|
+
/** Default `kid` for an env-backed dev key when none is supplied. */
|
|
32
|
+
export const AXTARY_DEFAULT_DEV_KEY_ID = "axtary-dev";
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the file-backed dev keyring path: an explicit override, else
|
|
35
|
+
* `$AXTARY_HOME/sdk-dev-keyring.json`, else project-local
|
|
36
|
+
* `.axtary/sdk-dev-keyring.json`.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveDevKeyringPath(options = {}) {
|
|
39
|
+
if (options.path) {
|
|
40
|
+
return options.path;
|
|
41
|
+
}
|
|
42
|
+
const env = options.env ?? process.env;
|
|
43
|
+
const home = env[AXTARY_HOME_ENV];
|
|
44
|
+
if (home && home.trim().length > 0) {
|
|
45
|
+
return join(home, AXTARY_DEV_KEYRING_FILENAME);
|
|
46
|
+
}
|
|
47
|
+
return join(".axtary", AXTARY_DEV_KEYRING_FILENAME);
|
|
48
|
+
}
|
|
49
|
+
function pemFromEnvValue(raw) {
|
|
50
|
+
const trimmed = raw.trim();
|
|
51
|
+
if (trimmed.includes("-----BEGIN")) {
|
|
52
|
+
return trimmed;
|
|
53
|
+
}
|
|
54
|
+
// Single-line base64 / base64url-encoded PEM (env-var friendly).
|
|
55
|
+
return Buffer.from(trimmed, "base64").toString("utf8");
|
|
56
|
+
}
|
|
57
|
+
async function envBackedKey(env) {
|
|
58
|
+
const raw = env[AXTARY_DEV_SIGNING_KEY_ENV];
|
|
59
|
+
if (!raw || raw.trim().length === 0) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const signingKey = createPrivateKey(pemFromEnvValue(raw));
|
|
63
|
+
const verificationKey = await exportJWK(createPublicKey(signingKey));
|
|
64
|
+
verificationKey.alg = "ES256";
|
|
65
|
+
const keyId = env[AXTARY_DEV_KEY_ID_ENV];
|
|
66
|
+
return {
|
|
67
|
+
signingKey,
|
|
68
|
+
verificationKey,
|
|
69
|
+
signingKeyId: keyId && keyId.trim().length > 0 ? keyId : AXTARY_DEFAULT_DEV_KEY_ID,
|
|
70
|
+
algorithm: "ES256",
|
|
71
|
+
source: "env",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a stable, persistent ES256 dev keypair with zero manual JWK code.
|
|
76
|
+
* Resolution order: AXTARY_DEV_SIGNING_KEY (env-backed, shared across
|
|
77
|
+
* processes) → a local keyring file (default `.axtary/sdk-dev-keyring.json`,
|
|
78
|
+
* `0600`, gitignored). Both verify across process restarts. Pass the result
|
|
79
|
+
* straight to the SDK: `createAxtary(await devKeypair())`.
|
|
80
|
+
*
|
|
81
|
+
* This is a development convenience. For production use a real issuer key or a
|
|
82
|
+
* hosted issuer; the file-backed key never leaves the machine and the env-backed
|
|
83
|
+
* key is whatever the operator provisions.
|
|
84
|
+
*/
|
|
85
|
+
export async function devKeypair(options = {}) {
|
|
86
|
+
const env = options.env ?? process.env;
|
|
87
|
+
const fromEnv = await envBackedKey(env);
|
|
88
|
+
if (fromEnv) {
|
|
89
|
+
return fromEnv;
|
|
90
|
+
}
|
|
91
|
+
const path = resolveDevKeyringPath(options);
|
|
92
|
+
const keyring = await loadOrCreateIssuerKeyring(path);
|
|
93
|
+
const signer = issuerSigningKey(keyring);
|
|
94
|
+
return {
|
|
95
|
+
signingKey: signer.privateKey,
|
|
96
|
+
verificationKey: { ...signer.publicJwk, alg: "ES256" },
|
|
97
|
+
signingKeyId: signer.kid,
|
|
98
|
+
algorithm: "ES256",
|
|
99
|
+
source: "file",
|
|
100
|
+
path,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=dev-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-key.js","sourceRoot":"","sources":["../src/dev-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PolicyDecision } from "./index.js";
|
|
2
|
+
export type PolicyDecisionExplanation = {
|
|
3
|
+
label: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
reviewerAction: "none" | "block" | "approval_required";
|
|
6
|
+
reasonDetails: Array<{
|
|
7
|
+
reason: string;
|
|
8
|
+
detail: string;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
export declare function explainDecision(decisionInput: PolicyDecision): PolicyDecisionExplanation;
|
|
12
|
+
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,mBAAmB,CAAC;IACvD,aAAa,EAAE,KAAK,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ,CAAC;AAEF,wBAAgB,eAAe,CAC7B,aAAa,EAAE,cAAc,GAC5B,yBAAyB,CAqB3B"}
|
package/dist/explain.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Deterministic, human-readable explanation of a policy decision.
|
|
2
|
+
//
|
|
3
|
+
// This is the canonical reason→prose map for Axtary. It lives in the base
|
|
4
|
+
// `@axtary/actionpass` package so both the SDK facade (`axtary.explain`) and the
|
|
5
|
+
// policy/CLI surfaces share one source of truth instead of duplicating prose.
|
|
6
|
+
// `@axtary/policy` re-exports `explainPolicyDecision` from here for backward
|
|
7
|
+
// compatibility. It is intentionally deterministic — no model call — so the same
|
|
8
|
+
// decision always produces the same explanation.
|
|
9
|
+
export function explainDecision(decisionInput) {
|
|
10
|
+
const reviewerAction = decisionInput.decision === "allow"
|
|
11
|
+
? "none"
|
|
12
|
+
: decisionInput.decision === "step_up"
|
|
13
|
+
? "approval_required"
|
|
14
|
+
: "block";
|
|
15
|
+
const label = decisionInput.decision === "allow"
|
|
16
|
+
? "Allowed"
|
|
17
|
+
: decisionInput.decision === "step_up"
|
|
18
|
+
? "Step-up required"
|
|
19
|
+
: "Denied";
|
|
20
|
+
return {
|
|
21
|
+
label,
|
|
22
|
+
reviewerAction,
|
|
23
|
+
summary: decisionSummary(decisionInput),
|
|
24
|
+
reasonDetails: decisionInput.reasons.map((reason) => ({
|
|
25
|
+
reason,
|
|
26
|
+
detail: reasonDetail(reason),
|
|
27
|
+
})),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function decisionSummary(decisionInput) {
|
|
31
|
+
if (decisionInput.decision === "allow") {
|
|
32
|
+
return "The normalized action is inside the active policy and can proceed through ActionPass issuance.";
|
|
33
|
+
}
|
|
34
|
+
if (decisionInput.decision === "step_up") {
|
|
35
|
+
return "The normalized action is in scope only after exact payload-bound reviewer approval.";
|
|
36
|
+
}
|
|
37
|
+
return "The normalized action is outside the active policy and must not execute.";
|
|
38
|
+
}
|
|
39
|
+
function reasonDetail(reason) {
|
|
40
|
+
if (reason === "github_pr_inside_policy") {
|
|
41
|
+
return "Base branch, changed paths, file count, and test evidence satisfy the GitHub PR rule.";
|
|
42
|
+
}
|
|
43
|
+
if (reason === "github_content_write_inside_policy") {
|
|
44
|
+
return "The write path and branch are inside the configured repository guardrails.";
|
|
45
|
+
}
|
|
46
|
+
if (reason === "github_review_protected_path_step_up") {
|
|
47
|
+
return "The inline review targets a protected path and requires exact payload-bound approval.";
|
|
48
|
+
}
|
|
49
|
+
if (reason === "github_review_comment_inside_policy") {
|
|
50
|
+
return "The inline review target and exact comment body satisfy the repository guardrails.";
|
|
51
|
+
}
|
|
52
|
+
if (reason === "github_check_run_inside_policy") {
|
|
53
|
+
return "The check name, commit SHA, state, conclusion, and output are structurally valid and payload-bound.";
|
|
54
|
+
}
|
|
55
|
+
if (reason === "github_issue_comment_inside_policy") {
|
|
56
|
+
return "The issue number and exact outbound comment body are structurally valid and payload-bound.";
|
|
57
|
+
}
|
|
58
|
+
if (reason === "postgres_select_inside_policy") {
|
|
59
|
+
return "The parameterized SELECT, database, tables, functions, predicate, and row cap are inside the configured read scope.";
|
|
60
|
+
}
|
|
61
|
+
if (reason === "postgres_unpredicated_scan_step_up") {
|
|
62
|
+
return "The SELECT has no row predicate and requires exact payload-bound approval before the adapter can connect.";
|
|
63
|
+
}
|
|
64
|
+
if (reason === "postgres_select_only") {
|
|
65
|
+
return "Only one parsed SELECT statement is executable through the Postgres connector.";
|
|
66
|
+
}
|
|
67
|
+
if (reason === "postgres_table_not_allowed") {
|
|
68
|
+
return "At least one referenced table is outside the configured Postgres allowlist.";
|
|
69
|
+
}
|
|
70
|
+
if (reason === "github_branch_inside_policy") {
|
|
71
|
+
return "The branch creation request is tied to the configured base branch.";
|
|
72
|
+
}
|
|
73
|
+
if (reason === "content_read_denied_path") {
|
|
74
|
+
return "The requested file path matches a protected content-read prefix.";
|
|
75
|
+
}
|
|
76
|
+
if (reason === "payload_touches_denied_path") {
|
|
77
|
+
return "The payload touches a path prefix that policy blocks outright.";
|
|
78
|
+
}
|
|
79
|
+
if (reason === "payload_touches_step_up_path") {
|
|
80
|
+
return "The payload touches a protected path prefix that requires exact review.";
|
|
81
|
+
}
|
|
82
|
+
if (reason === "payload_touches_protected_path") {
|
|
83
|
+
return "The payload touches a protected path prefix and requires exact review.";
|
|
84
|
+
}
|
|
85
|
+
if (reason === "file_count_exceeds_policy_limit") {
|
|
86
|
+
return "The PR changes more files than the active policy allows without review.";
|
|
87
|
+
}
|
|
88
|
+
if (reason === "tests_required_before_pr") {
|
|
89
|
+
return "The PR payload does not include passing test evidence required by policy.";
|
|
90
|
+
}
|
|
91
|
+
if (reason === "payload_declares_production_impact") {
|
|
92
|
+
return "The action declares production impact, so reviewer approval is required.";
|
|
93
|
+
}
|
|
94
|
+
if (reason === "slack_channel_not_allowed") {
|
|
95
|
+
return "The Slack channel is not in the configured allowlist.";
|
|
96
|
+
}
|
|
97
|
+
if (reason === "slack_external_recipient_step_up") {
|
|
98
|
+
return "The message reaches external recipients and must be reviewed exactly.";
|
|
99
|
+
}
|
|
100
|
+
if (reason === "provenance_unknown_egress") {
|
|
101
|
+
return "A covered outbound field has no authority-stamped source lineage, so policy fails closed.";
|
|
102
|
+
}
|
|
103
|
+
if (reason === "provenance_untrusted_egress") {
|
|
104
|
+
return "A covered outbound field derives from untrusted document or tool output and requires exact review.";
|
|
105
|
+
}
|
|
106
|
+
if (reason === "linear_status_requires_step_up") {
|
|
107
|
+
return "The Linear status transition targets a configured protected status.";
|
|
108
|
+
}
|
|
109
|
+
if (reason === "jira_status_requires_step_up") {
|
|
110
|
+
return "The Jira status transition targets a configured protected status.";
|
|
111
|
+
}
|
|
112
|
+
if (reason.endsWith("_project_not_allowed")) {
|
|
113
|
+
return "The issue project key is outside the configured tracker allowlist.";
|
|
114
|
+
}
|
|
115
|
+
if (reason === "docs_search_inside_policy") {
|
|
116
|
+
return "The document search request stays inside the configured workspace and result limit.";
|
|
117
|
+
}
|
|
118
|
+
if (reason === "docs_read_inside_policy") {
|
|
119
|
+
return "The document path and requested byte limit are inside the configured documentation scope.";
|
|
120
|
+
}
|
|
121
|
+
if (reason === "docs_workspace_not_allowed") {
|
|
122
|
+
return "The document workspace is outside the configured allowlist.";
|
|
123
|
+
}
|
|
124
|
+
if (reason === "docs_path_denied") {
|
|
125
|
+
return "The requested document path matches a protected documentation prefix.";
|
|
126
|
+
}
|
|
127
|
+
if (reason === "docs_path_not_allowed") {
|
|
128
|
+
return "The requested document path is outside the configured documentation prefixes.";
|
|
129
|
+
}
|
|
130
|
+
if (reason === "docs_result_limit_exceeds_policy") {
|
|
131
|
+
return "The search request asks for more document results than policy allows.";
|
|
132
|
+
}
|
|
133
|
+
if (reason === "docs_read_bytes_exceeds_policy") {
|
|
134
|
+
return "The read request asks for more document bytes than policy allows.";
|
|
135
|
+
}
|
|
136
|
+
if (reason === "payload_is_inside_current_actionpass_constraints") {
|
|
137
|
+
return "The payload is inside the constraints carried by the current ActionPass.";
|
|
138
|
+
}
|
|
139
|
+
if (reason.startsWith("unsupported_tool:")) {
|
|
140
|
+
return "The tool is not supported by the native policy evaluator and fails closed.";
|
|
141
|
+
}
|
|
142
|
+
if (reason.startsWith("base_branch_mismatch:")) {
|
|
143
|
+
return "The requested base branch differs from the configured branch and needs review.";
|
|
144
|
+
}
|
|
145
|
+
return "The active native policy emitted this reason for the normalized action.";
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../src/explain.ts"],"names":[],"mappings":"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"}
|