@amalgm/core 0.4.1 → 0.4.3

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 CHANGED
@@ -100,6 +100,8 @@ that composes an exact Core release with the other product releases.
100
100
  17. **Retirement is explicit subtraction.** Every retired Engine prefix stays
101
101
  named as evidence but is absent from the active route table, so dead
102
102
  compatibility surfaces cannot masquerade as unfinished product work.
103
+ The retired `/email` compatibility door is owned by neither Core nor
104
+ Shell; current-user delivery belongs to the standalone Channels product.
103
105
  18. **Machine setup is one trust rail with two entrances.** Browser polling and
104
106
  an approved setup code converge before the same registration, credential
105
107
  split, installation, canonical-runtime, and readiness path;
@@ -2,5 +2,6 @@ export * from './policy.js';
2
2
  export * from './claims.js';
3
3
  export * from './dpop.js';
4
4
  export * from './broker.js';
5
+ export * from './resource.js';
5
6
  export * from './key-transition.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAC5B,cAAc,qBAAqB,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,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
@@ -2,5 +2,6 @@ export * from './policy.js';
2
2
  export * from './claims.js';
3
3
  export * from './dpop.js';
4
4
  export * from './broker.js';
5
+ export * from './resource.js';
5
6
  export * from './key-transition.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -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;AAC5B,cAAc,qBAAqB,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,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
@@ -2,6 +2,8 @@
2
2
  export declare const AUTHORIZATION_POLICY: Readonly<{
3
3
  readonly 'amalgm-api-proxy': readonly string[];
4
4
  readonly 'amalgm-apps': readonly string[];
5
+ readonly 'amalgm-automations': readonly string[];
6
+ readonly 'amalgm-channels': readonly string[];
5
7
  }>;
6
8
  export type AuthorizationAudience = keyof typeof AUTHORIZATION_POLICY;
7
9
  export type AuthorizationScope = (typeof AUTHORIZATION_POLICY)[AuthorizationAudience][number];
@@ -1 +1 @@
1
- {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB;;;EAatB,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,oBAAoB,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9F,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAEtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,kBAAkB,EAAE,CAEhG;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,SAAS,kBAAkB,EAAE,CAQ/B;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;gBAE3C,IAAI,EAAE,iBAAiB,GAAG,mBAAmB;CAO1D"}
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB;;;;;EAuBtB,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAAG,MAAM,OAAO,oBAAoB,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9F,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAEtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,kBAAkB,EAAE,CAEhG;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,qBAAqB,EAC/B,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,SAAS,kBAAkB,EAAE,CAQ/B;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAAC;gBAE3C,IAAI,EAAE,iBAAiB,GAAG,mBAAmB;CAO1D"}
@@ -12,6 +12,16 @@ export const AUTHORIZATION_POLICY = Object.freeze({
12
12
  'apps:read',
13
13
  'apps:run',
14
14
  ]),
15
+ 'amalgm-automations': Object.freeze([
16
+ 'automations:read',
17
+ 'automations:write',
18
+ 'runs:execute',
19
+ 'runs:read',
20
+ ]),
21
+ 'amalgm-channels': Object.freeze([
22
+ 'channels:read',
23
+ 'channels:send',
24
+ ]),
15
25
  });
16
26
  export function isAuthorizationAudience(value) {
17
27
  return typeof value === 'string' && value in AUTHORIZATION_POLICY;
@@ -1 +1 @@
1
- {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC;QAChC,eAAe;QACf,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,SAAS;KACV,CAAC;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;QAC3B,aAAa;QACb,WAAW;QACX,UAAU;KACX,CAAC;CACM,CAAC,CAAC;AAKZ,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,oBAAoB,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAA+B,EAC/B,MAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAkC,CAAC;AACvE,CAAC;AAED,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,CAA0C;IAEvD,YAAY,IAA6C;QACvD,KAAK,CAAC,IAAI,KAAK,iBAAiB;YAC9B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,sDAAsD,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/identity/authorization/policy.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,kBAAkB,EAAE,MAAM,CAAC,MAAM,CAAC;QAChC,eAAe;QACf,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,SAAS;KACV,CAAC;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;QAC3B,aAAa;QACb,WAAW;QACX,UAAU;KACX,CAAC;IACF,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC;QAClC,kBAAkB;QAClB,mBAAmB;QACnB,cAAc;QACd,WAAW;KACZ,CAAC;IACF,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/B,eAAe;QACf,eAAe;KAChB,CAAC;CACM,CAAC,CAAC;AAKZ,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,oBAAoB,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAA+B;IAC/D,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAA+B,EAC/B,MAAyB;IAEzB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,wBAAwB,CAAC,mBAAmB,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAkC,CAAC;AACvE,CAAC;AAED,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACxC,IAAI,CAA0C;IAEvD,YAAY,IAA6C;QACvD,KAAK,CAAC,IAAI,KAAK,iBAAiB;YAC9B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,sDAAsD,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ import { type AuthorizationAudience, type AuthorizationScope } from './policy.js';
2
+ export interface VerifiedJwt {
3
+ readonly header: Readonly<Record<string, unknown>>;
4
+ readonly claims: Readonly<Record<string, unknown>>;
5
+ }
6
+ export interface MachineResourceVerifierPorts {
7
+ verifyAccessToken(token: string): Promise<VerifiedJwt>;
8
+ verifyDpopProof(proof: string): Promise<VerifiedJwt>;
9
+ jwkThumbprint(jwk: Readonly<Record<string, unknown>>): Promise<string>;
10
+ accessTokenHash(token: string): Promise<string>;
11
+ consumeProof(keyThumbprint: string, jti: string): Promise<boolean>;
12
+ nowSeconds(): number;
13
+ }
14
+ export interface MachineResourceRequest {
15
+ readonly authorization: string | null | undefined;
16
+ readonly dpop: string | null | undefined;
17
+ readonly method: string;
18
+ readonly url: string;
19
+ readonly issuer: string;
20
+ readonly audience: AuthorizationAudience;
21
+ readonly requiredScopes: readonly AuthorizationScope[];
22
+ }
23
+ export interface AuthorizedMachinePrincipal {
24
+ readonly userId: string;
25
+ readonly computerId: string;
26
+ readonly clientId: 'amalgm-shell';
27
+ readonly scopes: readonly AuthorizationScope[];
28
+ readonly keyThumbprint: string;
29
+ }
30
+ /**
31
+ * The one resource-server decision for every hosted Amalgm product. Crypto
32
+ * and shared replay persistence are injected; claims and DPoP law stay here.
33
+ */
34
+ export declare function authorizeMachineResource(request: MachineResourceRequest, ports: MachineResourceVerifierPorts): Promise<AuthorizedMachinePrincipal>;
35
+ export declare class MachineResourceAuthorizationError extends Error {
36
+ readonly code: string;
37
+ constructor(code: string);
38
+ }
39
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/identity/authorization/resource.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,4BAA4B;IAC3C,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACvD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrD,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,sBAAsB,EAC/B,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,0BAA0B,CAAC,CAwErC;AA8DD,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKzB"}
@@ -0,0 +1,132 @@
1
+ import { ACCESS_TOKEN_CLOCK_SKEW_SECONDS, ACCESS_TOKEN_TTL_SECONDS, DPOP_PROOF_MAX_AGE_SECONDS, scopesFromClaim, } from './claims.js';
2
+ import { normalizeDpopHtu, normalizeDpopMethod } from './dpop.js';
3
+ import { canonicalScopes, } from './policy.js';
4
+ /**
5
+ * The one resource-server decision for every hosted Amalgm product. Crypto
6
+ * and shared replay persistence are injected; claims and DPoP law stay here.
7
+ */
8
+ export async function authorizeMachineResource(request, ports) {
9
+ const token = dpopAccessToken(request.authorization);
10
+ const proofValue = requiredText(request.dpop, 'dpop_required');
11
+ const now = ports.nowSeconds();
12
+ if (!Number.isInteger(now))
13
+ throw new MachineResourceAuthorizationError('invalid_clock');
14
+ const access = await verified(() => ports.verifyAccessToken(token), 'invalid_access_token');
15
+ assertHeader(access.header, 'at+jwt', false);
16
+ const claims = access.claims;
17
+ if (claims.iss !== request.issuer || claims.aud !== request.audience) {
18
+ throw new MachineResourceAuthorizationError('access_target_mismatch');
19
+ }
20
+ const issuedAt = integerClaim(claims.iat, 'invalid_access_claims');
21
+ const expiresAt = integerClaim(claims.exp, 'invalid_access_claims');
22
+ if (issuedAt > now + ACCESS_TOKEN_CLOCK_SKEW_SECONDS
23
+ || expiresAt <= now - ACCESS_TOKEN_CLOCK_SKEW_SECONDS
24
+ || expiresAt - issuedAt > ACCESS_TOKEN_TTL_SECONDS)
25
+ throw new MachineResourceAuthorizationError('invalid_access_lifetime');
26
+ const userId = claimText(claims.sub, 'invalid_access_claims');
27
+ const computerId = claimText(claims.computer_id, 'invalid_access_claims');
28
+ if (claims.client_id !== 'amalgm-shell') {
29
+ throw new MachineResourceAuthorizationError('invalid_client');
30
+ }
31
+ claimText(claims.jti, 'invalid_access_claims');
32
+ const confirmation = record(claims.cnf, 'invalid_access_claims');
33
+ const keyThumbprint = claimText(confirmation.jkt, 'invalid_access_claims');
34
+ if (!/^[A-Za-z0-9_-]{43}$/.test(keyThumbprint)) {
35
+ throw new MachineResourceAuthorizationError('invalid_access_claims');
36
+ }
37
+ const rawScopes = scopesFromClaim(claims.scope);
38
+ let scopes;
39
+ try {
40
+ scopes = canonicalScopes(request.audience, rawScopes);
41
+ }
42
+ catch {
43
+ throw new MachineResourceAuthorizationError('invalid_scope');
44
+ }
45
+ const requiredScopes = canonicalScopes(request.audience, request.requiredScopes);
46
+ if (requiredScopes.some((scope) => !scopes.includes(scope))) {
47
+ throw new MachineResourceAuthorizationError('insufficient_scope');
48
+ }
49
+ const proof = await verified(() => ports.verifyDpopProof(proofValue), 'invalid_dpop_proof');
50
+ const publicJwk = assertHeader(proof.header, 'dpop+jwt', true);
51
+ const proofClaims = proof.claims;
52
+ if (proofClaims.htm !== normalizeDpopMethod(request.method)
53
+ || proofClaims.htu !== normalizeDpopHtu(request.url)
54
+ || proofClaims.ath !== await ports.accessTokenHash(token))
55
+ throw new MachineResourceAuthorizationError('dpop_target_mismatch');
56
+ const proofIat = integerClaim(proofClaims.iat, 'invalid_dpop_claims');
57
+ const proofJti = claimText(proofClaims.jti, 'invalid_dpop_claims');
58
+ if (Math.abs(now - proofIat) > DPOP_PROOF_MAX_AGE_SECONDS
59
+ || proofJti.length > 200)
60
+ throw new MachineResourceAuthorizationError('invalid_dpop_claims');
61
+ if (await ports.jwkThumbprint(publicJwk) !== keyThumbprint) {
62
+ throw new MachineResourceAuthorizationError('dpop_key_mismatch');
63
+ }
64
+ if (!await ports.consumeProof(keyThumbprint, proofJti)) {
65
+ throw new MachineResourceAuthorizationError('dpop_replayed');
66
+ }
67
+ return Object.freeze({
68
+ userId,
69
+ computerId,
70
+ clientId: 'amalgm-shell',
71
+ scopes,
72
+ keyThumbprint,
73
+ });
74
+ }
75
+ function dpopAccessToken(value) {
76
+ const match = value?.match(/^DPoP\s+(\S+)$/);
77
+ if (!match?.[1])
78
+ throw new MachineResourceAuthorizationError('dpop_access_required');
79
+ return match[1];
80
+ }
81
+ function assertHeader(header, type, requiresJwk) {
82
+ if (header.typ !== type || header.alg !== 'ES256') {
83
+ throw new MachineResourceAuthorizationError('invalid_jwt_header');
84
+ }
85
+ if (!requiresJwk)
86
+ return Object.freeze({});
87
+ const jwk = record(header.jwk, 'invalid_jwt_header');
88
+ if (jwk.kty !== 'EC'
89
+ || jwk.crv !== 'P-256'
90
+ || typeof jwk.x !== 'string'
91
+ || typeof jwk.y !== 'string'
92
+ || 'd' in jwk)
93
+ throw new MachineResourceAuthorizationError('invalid_jwt_header');
94
+ return jwk;
95
+ }
96
+ async function verified(operation, code) {
97
+ try {
98
+ return await operation();
99
+ }
100
+ catch {
101
+ throw new MachineResourceAuthorizationError(code);
102
+ }
103
+ }
104
+ function requiredText(value, code) {
105
+ if (typeof value !== 'string' || !value.trim()) {
106
+ throw new MachineResourceAuthorizationError(code);
107
+ }
108
+ return value.trim();
109
+ }
110
+ function claimText(value, code) {
111
+ return requiredText(value, code);
112
+ }
113
+ function integerClaim(value, code) {
114
+ if (!Number.isInteger(value))
115
+ throw new MachineResourceAuthorizationError(code);
116
+ return value;
117
+ }
118
+ function record(value, code) {
119
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
120
+ throw new MachineResourceAuthorizationError(code);
121
+ }
122
+ return value;
123
+ }
124
+ export class MachineResourceAuthorizationError extends Error {
125
+ code;
126
+ constructor(code) {
127
+ super(code);
128
+ this.name = 'MachineResourceAuthorizationError';
129
+ this.code = code;
130
+ }
131
+ }
132
+ //# sourceMappingURL=resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../../src/identity/authorization/resource.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@
9
9
  * under its own '/pty' handling).
10
10
  *
11
11
  * Counts, pinned by tests: 3 gateway prefixes, 5 gateway exact paths,
12
- * 3 port-monitor prefixes, 1 chat prefix, 22 active MCP prefixes.
12
+ * 3 port-monitor prefixes, 1 chat prefix, 21 active MCP prefixes.
13
13
  *
14
14
  * This is product configuration riding in the kernel repo for the
15
15
  * cutover: the table SHAPE is core's; these CONTENTS belong to the
@@ -23,8 +23,10 @@ import type { RouteTable } from './routing.js';
23
23
  * `/tasks` is the superseded SQLite scheduler facade; `/mcp-connections` is
24
24
  * the superseded second registry now represented by Tools connection records
25
25
  * plus credential references resolved by Core's machine-local vault; `/github`
26
- * is superseded by the next cloud integration and credential collection model.
26
+ * is superseded by the next cloud integration and credential collection model;
27
+ * `/email` was the in-process Notifications compatibility door and is replaced
28
+ * by the independently deployed Channels product.
27
29
  */
28
- export declare const retiredEngineRoutePrefixes: readonly ["/tasks", "/mcp-connections", "/github"];
30
+ export declare const retiredEngineRoutePrefixes: readonly ["/tasks", "/mcp-connections", "/github", "/email"];
29
31
  export declare const shippedRouteTable: RouteTable;
30
32
  //# sourceMappingURL=shipped-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shipped-routes.d.ts","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,oDAAqD,CAAC;AAE7F,eAAO,MAAM,iBAAiB,EAAE,UA6B/B,CAAC"}
1
+ {"version":3,"file":"shipped-routes.d.ts","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,8DAA+D,CAAC;AAEvG,eAAO,MAAM,iBAAiB,EAAE,UA4B/B,CAAC"}
@@ -9,7 +9,7 @@
9
9
  * under its own '/pty' handling).
10
10
  *
11
11
  * Counts, pinned by tests: 3 gateway prefixes, 5 gateway exact paths,
12
- * 3 port-monitor prefixes, 1 chat prefix, 22 active MCP prefixes.
12
+ * 3 port-monitor prefixes, 1 chat prefix, 21 active MCP prefixes.
13
13
  *
14
14
  * This is product configuration riding in the kernel repo for the
15
15
  * cutover: the table SHAPE is core's; these CONTENTS belong to the
@@ -22,9 +22,11 @@
22
22
  * `/tasks` is the superseded SQLite scheduler facade; `/mcp-connections` is
23
23
  * the superseded second registry now represented by Tools connection records
24
24
  * plus credential references resolved by Core's machine-local vault; `/github`
25
- * is superseded by the next cloud integration and credential collection model.
25
+ * is superseded by the next cloud integration and credential collection model;
26
+ * `/email` was the in-process Notifications compatibility door and is replaced
27
+ * by the independently deployed Channels product.
26
28
  */
27
- export const retiredEngineRoutePrefixes = ['/tasks', '/mcp-connections', '/github'];
29
+ export const retiredEngineRoutePrefixes = ['/tasks', '/mcp-connections', '/github', '/email'];
28
30
  export const shippedRouteTable = {
29
31
  gatewayPrefixes: ['/__amalgm', '/diagnostics', '/pty'],
30
32
  gatewayExactPaths: ['/', '/healthz', '/runtime-state', '/pty/session', '/pty/resize'],
@@ -42,7 +44,6 @@ export const shippedRouteTable = {
42
44
  '/state',
43
45
  '/local',
44
46
  '/credentials',
45
- '/email',
46
47
  '/slack',
47
48
  '/user-api-keys',
48
49
  '/apps',
@@ -1 +1 @@
1
- {"version":3,"file":"shipped-routes.js","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,kBAAkB,EAAE,SAAS,CAAU,CAAC;AAE7F,MAAM,CAAC,MAAM,iBAAiB,GAAe;IAC3C,eAAe,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC;IACtD,iBAAiB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,CAAC;IACrF,mBAAmB,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,eAAe,CAAC;IAC9D,YAAY,EAAE,CAAC,OAAO,CAAC;IACvB,WAAW,EAAE;QACX,MAAM;QACN,SAAS;QACT,cAAc;QACd,iBAAiB;QACjB,YAAY;QACZ,kBAAkB;QAClB,KAAK;QACL,UAAU;QACV,QAAQ;QACR,QAAQ;QACR,cAAc;QACd,QAAQ;QACR,QAAQ;QACR,gBAAgB;QAChB,OAAO;QACP,YAAY;QACZ,SAAS;QACT,eAAe;QACf,gBAAgB;QAChB,SAAS;QACT,mBAAmB;QACnB,UAAU;KACX;CACF,CAAC"}
1
+ {"version":3,"file":"shipped-routes.js","sourceRoot":"","sources":["../../src/transport/shipped-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,QAAQ,EAAE,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAEvG,MAAM,CAAC,MAAM,iBAAiB,GAAe;IAC3C,eAAe,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC;IACtD,iBAAiB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,CAAC;IACrF,mBAAmB,EAAE,CAAC,YAAY,EAAE,QAAQ,EAAE,eAAe,CAAC;IAC9D,YAAY,EAAE,CAAC,OAAO,CAAC;IACvB,WAAW,EAAE;QACX,MAAM;QACN,SAAS;QACT,cAAc;QACd,iBAAiB;QACjB,YAAY;QACZ,kBAAkB;QAClB,KAAK;QACL,UAAU;QACV,QAAQ;QACR,QAAQ;QACR,cAAc;QACd,QAAQ;QACR,gBAAgB;QAChB,OAAO;QACP,YAAY;QACZ,SAAS;QACT,eAAe;QACf,gBAAgB;QAChB,SAAS;QACT,mBAAmB;QACnB,UAAU;KACX;CACF,CAAC"}
@@ -36,6 +36,15 @@ Import `@amalgm/core/authorization` for:
36
36
  - URL and method normalization;
37
37
  - the closed initial-pin, ordinary-reuse, and explicit-recovery transition;
38
38
  - `CredentialBroker`, whose issuer and signer effects are injected by Shell.
39
+ - `authorizeMachineResource`, whose crypto and replay effects are injected by
40
+ a hosted product while Core makes the one claims-and-DPoP decision.
41
+
42
+ The closed service grants are:
43
+
44
+ - `amalgm-api-proxy`: one scope per supported LLM provider;
45
+ - `amalgm-apps`: read, run, and manage apps;
46
+ - `amalgm-automations`: read/write definitions and read/execute runs;
47
+ - `amalgm-channels`: read delivery history and send to the current user.
39
48
 
40
49
  The default access lifetime is 15 minutes. Cached credentials refresh at least
41
50
  two minutes early, with up to 30 seconds of jitter. DPoP proofs are one-request
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/core",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
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",