@amalgm/core 0.3.1 → 0.3.2
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/PURPOSE.md +8 -3
- package/README.md +5 -0
- package/dist/identity/authorization/index.d.ts +1 -0
- package/dist/identity/authorization/index.d.ts.map +1 -1
- package/dist/identity/authorization/index.js +1 -0
- package/dist/identity/authorization/index.js.map +1 -1
- package/dist/identity/authorization/key-transition.d.ts +19 -0
- package/dist/identity/authorization/key-transition.d.ts.map +1 -0
- package/dist/identity/authorization/key-transition.js +44 -0
- package/dist/identity/authorization/key-transition.js.map +1 -0
- package/docs/authorization.md +4 -0
- package/package.json +2 -3
package/PURPOSE.md
CHANGED
|
@@ -122,9 +122,14 @@ source until each module is cut over.
|
|
|
122
122
|
re-resolved against the verified catalog on every resume, and a changed
|
|
123
123
|
plan becomes durable only after its installation and runtime prove healthy.
|
|
124
124
|
22. **User access is short-lived and machine-bound.** A durable machine
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
credential may mint an audience- and scope-exact access token, but every
|
|
126
|
+
resource request also proves possession of that machine's key; Core owns
|
|
127
|
+
the claims and renewal law while hosts own keys, storage, signing, and HTTP.
|
|
128
|
+
23. **Machine key replacement is an explicit recovery, never startup repair.**
|
|
129
|
+
Ordinary registration may pin an absent key or reuse the same key; replacing
|
|
130
|
+
it requires fresh user approval, proof of the candidate private key, and a
|
|
131
|
+
conditional swap from the expected old thumbprint. A completed swap is safe
|
|
132
|
+
to retry, and corrupt local key material remains visible until that ceremony.
|
|
128
133
|
|
|
129
134
|
## Five surfaces, one behavior
|
|
130
135
|
|
package/README.md
CHANGED
|
@@ -32,3 +32,8 @@ Rules of the house: zero runtime dependencies; nothing in `src/` imports
|
|
|
32
32
|
shipped engine (the production source until cutover — PURPOSE.md axiom 9);
|
|
33
33
|
tests are exams — each states a law, and a mutant must be convicted by at
|
|
34
34
|
least one.
|
|
35
|
+
|
|
36
|
+
Package releases use `.github/workflows/publish-package.yml`. A reviewed main
|
|
37
|
+
commit is tagged exactly `v<package version>`; trusted npm publishing stages
|
|
38
|
+
that package, and a maintainer approves the staged artifact with 2FA. Manual
|
|
39
|
+
dirty-worktree publishing is not a release path.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/identity/authorization/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const AUTHORIZATION_KEY_RECOVERY_PURPOSE: "authorization_key_recovery";
|
|
2
|
+
export type AuthorizationKeyTransition = 'pin' | 'unchanged' | 'rotate';
|
|
3
|
+
export interface AuthorizationKeyTransitionInput {
|
|
4
|
+
readonly currentKeyThumbprint: string | null;
|
|
5
|
+
readonly presentedKeyThumbprint: string;
|
|
6
|
+
readonly recovery?: Readonly<{
|
|
7
|
+
readonly purpose: typeof AUTHORIZATION_KEY_RECOVERY_PURPOSE;
|
|
8
|
+
readonly expectedKeyThumbprint: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
/** One closed rule for initial pinning, ordinary reuse, and explicit recovery. */
|
|
12
|
+
export declare function authorizationKeyTransition(input: AuthorizationKeyTransitionInput): AuthorizationKeyTransition;
|
|
13
|
+
export declare function isAuthorizationKeyThumbprint(value: unknown): value is string;
|
|
14
|
+
export type AuthorizationKeyTransitionErrorCode = 'invalid_key_thumbprint' | 'invalid_recovery_purpose' | 'recovery_key_unchanged' | 'recovery_basis_mismatch' | 'key_mismatch';
|
|
15
|
+
export declare class AuthorizationKeyTransitionError extends Error {
|
|
16
|
+
readonly code: AuthorizationKeyTransitionErrorCode;
|
|
17
|
+
constructor(code: AuthorizationKeyTransitionErrorCode);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=key-transition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-transition.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/key-transition.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,EAAG,4BAAqC,CAAC;AAExF,MAAM,MAAM,0BAA0B,GAAG,KAAK,GAAG,WAAW,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,kCAAkC,CAAC;QAC5D,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;KACxC,CAAC,CAAC;CACJ;AAED,kFAAkF;AAClF,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,0BAA0B,CAsB5B;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5E;AAQD,MAAM,MAAM,mCAAmC,GAC3C,wBAAwB,GACxB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,cAAc,CAAC;AAEnB,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,QAAQ,CAAC,IAAI,EAAE,mCAAmC,CAAC;gBAEvC,IAAI,EAAE,mCAAmC;CAKtD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const AUTHORIZATION_KEY_RECOVERY_PURPOSE = 'authorization_key_recovery';
|
|
2
|
+
/** One closed rule for initial pinning, ordinary reuse, and explicit recovery. */
|
|
3
|
+
export function authorizationKeyTransition(input) {
|
|
4
|
+
assertKeyThumbprint(input.presentedKeyThumbprint);
|
|
5
|
+
if (input.currentKeyThumbprint !== null)
|
|
6
|
+
assertKeyThumbprint(input.currentKeyThumbprint);
|
|
7
|
+
if (input.recovery) {
|
|
8
|
+
assertKeyThumbprint(input.recovery.expectedKeyThumbprint);
|
|
9
|
+
if (input.recovery.purpose !== AUTHORIZATION_KEY_RECOVERY_PURPOSE) {
|
|
10
|
+
throw new AuthorizationKeyTransitionError('invalid_recovery_purpose');
|
|
11
|
+
}
|
|
12
|
+
if (input.presentedKeyThumbprint === input.recovery.expectedKeyThumbprint) {
|
|
13
|
+
throw new AuthorizationKeyTransitionError('recovery_key_unchanged');
|
|
14
|
+
}
|
|
15
|
+
if (input.currentKeyThumbprint === input.presentedKeyThumbprint)
|
|
16
|
+
return 'unchanged';
|
|
17
|
+
if (input.currentKeyThumbprint !== input.recovery.expectedKeyThumbprint) {
|
|
18
|
+
throw new AuthorizationKeyTransitionError('recovery_basis_mismatch');
|
|
19
|
+
}
|
|
20
|
+
return 'rotate';
|
|
21
|
+
}
|
|
22
|
+
if (input.currentKeyThumbprint === null)
|
|
23
|
+
return 'pin';
|
|
24
|
+
if (input.currentKeyThumbprint === input.presentedKeyThumbprint)
|
|
25
|
+
return 'unchanged';
|
|
26
|
+
throw new AuthorizationKeyTransitionError('key_mismatch');
|
|
27
|
+
}
|
|
28
|
+
export function isAuthorizationKeyThumbprint(value) {
|
|
29
|
+
return typeof value === 'string' && /^[A-Za-z0-9_-]{43}$/.test(value);
|
|
30
|
+
}
|
|
31
|
+
function assertKeyThumbprint(value) {
|
|
32
|
+
if (!isAuthorizationKeyThumbprint(value)) {
|
|
33
|
+
throw new AuthorizationKeyTransitionError('invalid_key_thumbprint');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class AuthorizationKeyTransitionError extends Error {
|
|
37
|
+
code;
|
|
38
|
+
constructor(code) {
|
|
39
|
+
super(code);
|
|
40
|
+
this.name = 'AuthorizationKeyTransitionError';
|
|
41
|
+
this.code = code;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=key-transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-transition.js","sourceRoot":"","sources":["../../../src/identity/authorization/key-transition.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kCAAkC,GAAG,4BAAqC,CAAC;AAaxF,kFAAkF;AAClF,MAAM,UAAU,0BAA0B,CACxC,KAAsC;IAEtC,mBAAmB,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,oBAAoB,KAAK,IAAI;QAAE,mBAAmB,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAEzF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,kCAAkC,EAAE,CAAC;YAClE,MAAM,IAAI,+BAA+B,CAAC,0BAA0B,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,KAAK,CAAC,sBAAsB,KAAK,KAAK,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YAC1E,MAAM,IAAI,+BAA+B,CAAC,wBAAwB,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,KAAK,CAAC,oBAAoB,KAAK,KAAK,CAAC,sBAAsB;YAAE,OAAO,WAAW,CAAC;QACpF,IAAI,KAAK,CAAC,oBAAoB,KAAK,KAAK,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC;YACxE,MAAM,IAAI,+BAA+B,CAAC,yBAAyB,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,KAAK,CAAC,oBAAoB,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,KAAK,CAAC,oBAAoB,KAAK,KAAK,CAAC,sBAAsB;QAAE,OAAO,WAAW,CAAC;IACpF,MAAM,IAAI,+BAA+B,CAAC,cAAc,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAc;IACzD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,+BAA+B,CAAC,wBAAwB,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AASD,MAAM,OAAO,+BAAgC,SAAQ,KAAK;IAC/C,IAAI,CAAsC;IAEnD,YAAY,IAAyC;QACnD,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
package/docs/authorization.md
CHANGED
|
@@ -20,6 +20,9 @@ needs.
|
|
|
20
20
|
single-flights concurrent renewal, and performs no silent auth fallback.
|
|
21
21
|
5. Audiences and scopes are closed sets. Adding a consumer changes this module
|
|
22
22
|
and its tests before any issuer or verifier accepts it.
|
|
23
|
+
6. A machine key is pinned once. Replacement requires an explicit, browser-
|
|
24
|
+
approved recovery bound to the expected old thumbprint and a different
|
|
25
|
+
candidate key; retrying an already completed replacement is idempotent.
|
|
23
26
|
|
|
24
27
|
## Public surface
|
|
25
28
|
|
|
@@ -28,6 +31,7 @@ Import `@amalgm/core/authorization` for:
|
|
|
28
31
|
- `AUTHORIZATION_POLICY` and the audience/scope types;
|
|
29
32
|
- access-token and DPoP claim contracts;
|
|
30
33
|
- URL and method normalization;
|
|
34
|
+
- the closed initial-pin, ordinary-reuse, and explicit-recovery transition;
|
|
31
35
|
- `CredentialBroker`, whose issuer and signer effects are injected by Shell.
|
|
32
36
|
|
|
33
37
|
The default access lifetime is 15 minutes. Cached credentials refresh at least
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "The Amalgm kernel SDK — identity, transport, supervision, and host capability contracts behind host-injected adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -67,8 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "tsc -p tsconfig.json",
|
|
70
|
-
"test": "npm run build && node --test
|
|
71
|
-
"test:parity": "npm run build && node --test tests/parity/",
|
|
70
|
+
"test": "npm run build && node --test",
|
|
72
71
|
"examine:machine-setup": "npm run build && node --test tests/supervision/installation-plan.test.js tests/machine-setup/examination.test.js",
|
|
73
72
|
"examine:user-ground": "npm run build && npm --prefix ../amalgm-live run build && node --test examinations/user-ground/login.examination.mjs"
|
|
74
73
|
},
|