@ontrails/permits 1.0.0-beta.12

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.
Files changed (69) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/.turbo/turbo-lint.log +3 -0
  3. package/.turbo/turbo-typecheck.log +1 -0
  4. package/CHANGELOG.md +19 -0
  5. package/dist/adapter.d.ts +26 -0
  6. package/dist/adapter.d.ts.map +1 -0
  7. package/dist/adapter.js +2 -0
  8. package/dist/adapter.js.map +1 -0
  9. package/dist/adapters/jwt.d.ts +25 -0
  10. package/dist/adapters/jwt.d.ts.map +1 -0
  11. package/dist/adapters/jwt.js +148 -0
  12. package/dist/adapters/jwt.js.map +1 -0
  13. package/dist/auth-layer.d.ts +18 -0
  14. package/dist/auth-layer.d.ts.map +1 -0
  15. package/dist/auth-layer.js +56 -0
  16. package/dist/auth-layer.js.map +1 -0
  17. package/dist/auth-service.d.ts +10 -0
  18. package/dist/auth-service.d.ts.map +1 -0
  19. package/dist/auth-service.js +21 -0
  20. package/dist/auth-service.js.map +1 -0
  21. package/dist/errors.d.ts +15 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +15 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/extraction.d.ts +20 -0
  26. package/dist/extraction.d.ts.map +1 -0
  27. package/dist/extraction.js +2 -0
  28. package/dist/extraction.js.map +1 -0
  29. package/dist/index.d.ts +11 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +11 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/permit.d.ts +26 -0
  34. package/dist/permit.d.ts.map +1 -0
  35. package/dist/permit.js +17 -0
  36. package/dist/permit.js.map +1 -0
  37. package/dist/rules.d.ts +47 -0
  38. package/dist/rules.d.ts.map +1 -0
  39. package/dist/rules.js +127 -0
  40. package/dist/rules.js.map +1 -0
  41. package/dist/testing.d.ts +20 -0
  42. package/dist/testing.d.ts.map +1 -0
  43. package/dist/testing.js +22 -0
  44. package/dist/testing.js.map +1 -0
  45. package/dist/trails/auth-verify.d.ts +22 -0
  46. package/dist/trails/auth-verify.d.ts.map +1 -0
  47. package/dist/trails/auth-verify.js +85 -0
  48. package/dist/trails/auth-verify.js.map +1 -0
  49. package/package.json +21 -0
  50. package/src/__tests__/adapter.test.ts +338 -0
  51. package/src/__tests__/auth-layer.test.ts +130 -0
  52. package/src/__tests__/auth-service.test.ts +62 -0
  53. package/src/__tests__/auth-verify.test.ts +277 -0
  54. package/src/__tests__/permit.test.ts +122 -0
  55. package/src/__tests__/rules.test.ts +239 -0
  56. package/src/__tests__/testing.test.ts +57 -0
  57. package/src/adapter.ts +35 -0
  58. package/src/adapters/jwt.ts +230 -0
  59. package/src/auth-layer.ts +80 -0
  60. package/src/auth-service.ts +25 -0
  61. package/src/errors.ts +18 -0
  62. package/src/extraction.ts +19 -0
  63. package/src/index.ts +14 -0
  64. package/src/permit.ts +26 -0
  65. package/src/rules.ts +183 -0
  66. package/src/testing.ts +34 -0
  67. package/src/trails/auth-verify.ts +97 -0
  68. package/tsconfig.json +9 -0
  69. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1 @@
1
+ $ tsc -b
@@ -0,0 +1,3 @@
1
+ $ oxlint ./src
2
+ Found 0 warnings and 0 errors.
3
+ Finished in 44ms on 18 files with 93 rules using 24 threads.
@@ -0,0 +1 @@
1
+ $ tsc --noEmit
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # @ontrails/permits
2
+
3
+ ## 1.0.0-beta.12
4
+
5
+ ### Minor Changes
6
+
7
+ - Complete trifecta for config, permits, and crumbs (formerly tracks)
8
+
9
+ - **config**: Add `configService`, `config.layer`, `config.trail`, and `config.workspace` trails with full `defineConfig`, `resolve`, `describe`, `explain`, `doctor`, and code generation support
10
+ - **permits**: Add `authService` and `auth.verify` trail for runtime authorization checks
11
+ - **crumbs**: Rename tracks to crumbs; add `crumbsService` and `crumbs.status` trail for structured event tracking
12
+ - **cli**: Fix build flag handling and improve bootstrap scaffolding
13
+ - **testing**: Expand test context helpers and example-based testing utilities
14
+ - **core/mcp/http**: Internal alignment for service and composition updates
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+ - @ontrails/core@1.0.0-beta.12
@@ -0,0 +1,26 @@
1
+ import type { Result } from '@ontrails/core';
2
+ import type { PermitExtractionInput } from './extraction.js';
3
+ import type { Permit } from './permit.js';
4
+ /**
5
+ * @deprecated Use {@link PermitExtractionInput} instead. Kept as an alias
6
+ * for backward compatibility during migration.
7
+ */
8
+ export type AuthCredentials = PermitExtractionInput;
9
+ /** Errors from auth adapters. */
10
+ export interface AuthError {
11
+ readonly code: 'expired_token' | 'insufficient_scope' | 'invalid_token' | 'missing_credentials';
12
+ readonly message: string;
13
+ }
14
+ /**
15
+ * Auth adapter port. Given extraction input, produce a permit or an error.
16
+ *
17
+ * The adapter receives the full {@link PermitExtractionInput} — surface,
18
+ * headers, requestId, and credential fields — so it can make richer
19
+ * decisions (e.g., rate-limit by surface or correlate via requestId).
20
+ *
21
+ * Deliberately narrow — no session management, no token refresh.
22
+ */
23
+ export interface AuthAdapter {
24
+ readonly authenticate: (input: PermitExtractionInput) => Promise<Result<Permit | null, AuthError>>;
25
+ }
26
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,iCAAiC;AACjC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EACT,eAAe,GACf,oBAAoB,GACpB,eAAe,GACf,qBAAqB,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,CACrB,KAAK,EAAE,qBAAqB,KACzB,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;CAChD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import type { AuthAdapter } from '../adapter.js';
2
+ /** Configuration for the JWT auth adapter. */
3
+ export interface JwtAdapterOptions {
4
+ /** HMAC secret for HS256 verification. */
5
+ readonly secret?: string;
6
+ /** JWKS endpoint for RS256/ES256 (not yet implemented). */
7
+ readonly jwksUrl?: string;
8
+ /** Expected issuer claim. */
9
+ readonly issuer?: string;
10
+ /** Expected audience claim. */
11
+ readonly audience?: string;
12
+ /** Claim containing scopes (default: 'scope'). */
13
+ readonly scopesClaim?: string;
14
+ /** Claim containing roles (default: 'roles'). */
15
+ readonly rolesClaim?: string;
16
+ }
17
+ /**
18
+ * Create a JWT auth adapter using Bun's native crypto.
19
+ *
20
+ * Verifies HS256-signed JWTs, extracts claims into a Permit, and checks
21
+ * issuer/audience when configured. Returns `Result.ok(null)` when no
22
+ * credentials are provided.
23
+ */
24
+ export declare const createJwtAdapter: (options: JwtAdapterOptions) => AuthAdapter;
25
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/adapters/jwt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,eAAe,CAAC;AAI5D,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AA2LD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,iBAAiB,KAAG,WAe7D,CAAC"}
@@ -0,0 +1,148 @@
1
+ import { Result } from '@ontrails/core';
2
+ // ---------------------------------------------------------------------------
3
+ // Helpers (defined before callers)
4
+ // ---------------------------------------------------------------------------
5
+ const authErr = (code, message) => Result.err({ code, message });
6
+ /** Base64url-decode a string to bytes. */
7
+ const base64urlDecode = (input) => {
8
+ const padded = input
9
+ .replaceAll('-', '+')
10
+ .replaceAll('_', '/')
11
+ .padEnd(input.length + ((4 - (input.length % 4)) % 4), '=');
12
+ const binary = atob(padded);
13
+ const bytes = new Uint8Array(binary.length);
14
+ for (let i = 0; i < binary.length; i += 1) {
15
+ bytes[i] = binary.codePointAt(i) ?? 0;
16
+ }
17
+ return bytes;
18
+ };
19
+ /** Decode a JWT payload without verifying the signature. */
20
+ const decodePayload = (token) => {
21
+ const parts = token.split('.');
22
+ if (parts.length !== 3) {
23
+ return undefined;
24
+ }
25
+ try {
26
+ const json = new TextDecoder().decode(base64urlDecode(parts[1] ?? ''));
27
+ return JSON.parse(json);
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ };
33
+ /** Import a secret as an HMAC CryptoKey. */
34
+ const importHmacKey = (secret) => {
35
+ const encoder = new TextEncoder();
36
+ return crypto.subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, ['verify']);
37
+ };
38
+ /** Verify the HMAC-SHA256 signature of a JWT. */
39
+ const verifyHmacSignature = (token, key) => {
40
+ const lastDot = token.lastIndexOf('.');
41
+ if (lastDot === -1) {
42
+ return Promise.resolve(false);
43
+ }
44
+ const data = token.slice(0, lastDot);
45
+ const signature = base64urlDecode(token.slice(lastDot + 1));
46
+ const encoder = new TextEncoder();
47
+ return crypto.subtle.verify('HMAC', key, signature.buffer, encoder.encode(data));
48
+ };
49
+ /** Validate standard claims (exp, iss, aud). */
50
+ const validateClaims = (payload, options) => {
51
+ if (payload.exp !== undefined &&
52
+ payload.exp < Math.floor(Date.now() / 1000)) {
53
+ return { code: 'expired_token', message: 'Token has expired' };
54
+ }
55
+ if (options.issuer && payload.iss !== options.issuer) {
56
+ return { code: 'invalid_token', message: 'Issuer mismatch' };
57
+ }
58
+ if (options.audience) {
59
+ const { aud } = payload;
60
+ const matches = Array.isArray(aud)
61
+ ? aud.includes(options.audience)
62
+ : aud === options.audience;
63
+ if (!matches) {
64
+ return { code: 'invalid_token', message: 'Audience mismatch' };
65
+ }
66
+ }
67
+ return undefined;
68
+ };
69
+ /** Extract scopes from a payload claim (space-separated string or array). */
70
+ const extractScopes = (payload, claim) => {
71
+ const raw = payload[claim];
72
+ if (typeof raw === 'string') {
73
+ return raw.split(' ').filter(Boolean);
74
+ }
75
+ if (Array.isArray(raw)) {
76
+ return raw.filter((s) => typeof s === 'string' && s.length > 0);
77
+ }
78
+ return [];
79
+ };
80
+ /** Extract roles from a payload claim (string array). */
81
+ const extractRoles = (payload, claim) => {
82
+ const raw = payload[claim];
83
+ if (!Array.isArray(raw)) {
84
+ return undefined;
85
+ }
86
+ return raw.filter((r) => typeof r === 'string');
87
+ };
88
+ /** Build a Permit from a validated JWT payload. */
89
+ const buildPermit = (payload, options) => {
90
+ if (!payload.sub) {
91
+ return authErr('invalid_token', 'Missing subject claim (sub)');
92
+ }
93
+ const roles = extractRoles(payload, options.rolesClaim ?? 'roles');
94
+ return Result.ok({
95
+ id: payload.sub,
96
+ scopes: extractScopes(payload, options.scopesClaim ?? 'scope'),
97
+ ...(roles ? { roles } : {}),
98
+ });
99
+ };
100
+ /** Verify the signature and return the decoded payload, or an error. */
101
+ const decodeAndVerify = async (token, secret) => {
102
+ const payload = decodePayload(token);
103
+ if (!payload) {
104
+ return authErr('invalid_token', 'Malformed JWT');
105
+ }
106
+ try {
107
+ const key = await importHmacKey(secret);
108
+ const valid = await verifyHmacSignature(token, key);
109
+ return valid
110
+ ? Result.ok(payload)
111
+ : authErr('invalid_token', 'Invalid signature');
112
+ }
113
+ catch {
114
+ return authErr('invalid_token', 'Malformed token signature');
115
+ }
116
+ };
117
+ /** Validate claims and build a permit from a verified payload. */
118
+ const payloadToPermit = (payload, options) => {
119
+ const claimError = validateClaims(payload, options);
120
+ if (claimError) {
121
+ return Result.err(claimError);
122
+ }
123
+ return buildPermit(payload, options);
124
+ };
125
+ // ---------------------------------------------------------------------------
126
+ // Factory
127
+ // ---------------------------------------------------------------------------
128
+ /**
129
+ * Create a JWT auth adapter using Bun's native crypto.
130
+ *
131
+ * Verifies HS256-signed JWTs, extracts claims into a Permit, and checks
132
+ * issuer/audience when configured. Returns `Result.ok(null)` when no
133
+ * credentials are provided.
134
+ */
135
+ export const createJwtAdapter = (options) => {
136
+ const authenticate = async (input) => {
137
+ if (!input.bearerToken) {
138
+ return Result.ok(null);
139
+ }
140
+ if (!options.secret) {
141
+ return authErr('invalid_token', 'No secret configured');
142
+ }
143
+ const decoded = await decodeAndVerify(input.bearerToken, options.secret);
144
+ return decoded.isErr() ? decoded : payloadToPermit(decoded.value, options);
145
+ };
146
+ return { authenticate };
147
+ };
148
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/adapters/jwt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Layer } from '@ontrails/core';
2
+ /**
3
+ * A {@link Layer} that enforces permit scopes declared on trails.
4
+ *
5
+ * The layer reads the trail's `permit` field (a `PermitRequirement`):
6
+ *
7
+ * - If `permit` is `'public'` or `undefined` the layer passes through.
8
+ * - If `permit` has `scopes`, the layer checks that `ctx.permit` contains
9
+ * all required scopes. A superset is fine; missing scopes produce a
10
+ * `PermitError`.
11
+ *
12
+ * Because `ctx.follow()` re-enters `executeTrail` (which applies layers),
13
+ * this layer automatically re-checks on every invocation in a follow chain.
14
+ * No special follow-chain handling is needed — it is built into the
15
+ * architecture.
16
+ */
17
+ export declare const authLayer: Layer;
18
+ //# sourceMappingURL=auth-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-layer.d.ts","sourceRoot":"","sources":["../src/auth-layer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AA6B5C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,EAAE,KAkCvB,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { Result } from '@ontrails/core';
2
+ import { PermitError } from './errors.js';
3
+ import { getPermit } from './permit.js';
4
+ // ---------------------------------------------------------------------------
5
+ // Helpers (defined before callers — no use-before-define)
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Returns `true` when the permit requirement means "no enforcement needed."
9
+ * Either the trail hasn't declared a permit posture or has explicitly
10
+ * opted out with `'public'`.
11
+ */
12
+ const isPassThrough = (requirement) => requirement === undefined || requirement === 'public';
13
+ /** Returns scopes present in `required` but absent from `held`. */
14
+ const findMissing = (required, held) => required.filter((s) => !held.includes(s));
15
+ // ---------------------------------------------------------------------------
16
+ // Auth layer
17
+ // ---------------------------------------------------------------------------
18
+ /**
19
+ * A {@link Layer} that enforces permit scopes declared on trails.
20
+ *
21
+ * The layer reads the trail's `permit` field (a `PermitRequirement`):
22
+ *
23
+ * - If `permit` is `'public'` or `undefined` the layer passes through.
24
+ * - If `permit` has `scopes`, the layer checks that `ctx.permit` contains
25
+ * all required scopes. A superset is fine; missing scopes produce a
26
+ * `PermitError`.
27
+ *
28
+ * Because `ctx.follow()` re-enters `executeTrail` (which applies layers),
29
+ * this layer automatically re-checks on every invocation in a follow chain.
30
+ * No special follow-chain handling is needed — it is built into the
31
+ * architecture.
32
+ */
33
+ export const authLayer = {
34
+ description: 'Enforces permit scopes declared on trails',
35
+ name: 'auth',
36
+ wrap: (_trail, impl) => {
37
+ const requirement = _trail.permit;
38
+ if (isPassThrough(requirement)) {
39
+ return impl;
40
+ }
41
+ return (input, ctx) => {
42
+ const permit = getPermit(ctx);
43
+ if (!permit) {
44
+ return Promise.resolve(Result.err(new PermitError('No permit provided')));
45
+ }
46
+ const missing = findMissing(requirement.scopes, permit.scopes);
47
+ if (missing.length > 0) {
48
+ return Promise.resolve(Result.err(new PermitError(`Missing scopes: ${missing.join(', ')}`, {
49
+ context: { missing, required: requirement.scopes },
50
+ })));
51
+ }
52
+ return Promise.resolve(impl(input, ctx));
53
+ };
54
+ },
55
+ };
56
+ //# sourceMappingURL=auth-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-layer.js","sourceRoot":"","sources":["../src/auth-layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,8EAA8E;AAC9E,0DAA0D;AAC1D,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,aAAa,GAAG,CACpB,WAAoB,EACiB,EAAE,CACvC,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,QAAQ,CAAC;AAExD,mEAAmE;AACnE,MAAM,WAAW,GAAG,CAClB,QAA2B,EAC3B,IAAuB,EACJ,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAElE,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,WAAW,EAAE,2CAA2C;IACxD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;QAElC,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAE9B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,OAAO,CAAC,OAAO,CACpB,MAAM,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAClD,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAE/D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO,OAAO,CAAC,OAAO,CACpB,MAAM,CAAC,GAAG,CACR,IAAI,WAAW,CAAC,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE;oBACvD,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE;iBACnD,CAAC,CACH,CACF,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { AuthAdapter } from './adapter.js';
2
+ /**
3
+ * Auth service — manages the auth adapter lifecycle.
4
+ *
5
+ * The v1 factory returns a no-op adapter that always succeeds (null permit).
6
+ * Real adapter configuration will come through `ServiceSpec.config` (TRL-91).
7
+ * The mock factory provides a synthetic adapter that always succeeds.
8
+ */
9
+ export declare const authService: import("@ontrails/core").Service<AuthAdapter>;
10
+ //# sourceMappingURL=auth-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../src/auth-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,+CAatB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Result, service } from '@ontrails/core';
2
+ /**
3
+ * Auth service — manages the auth adapter lifecycle.
4
+ *
5
+ * The v1 factory returns a no-op adapter that always succeeds (null permit).
6
+ * Real adapter configuration will come through `ServiceSpec.config` (TRL-91).
7
+ * The mock factory provides a synthetic adapter that always succeeds.
8
+ */
9
+ export const authService = service('auth', {
10
+ create: (_svc) => Result.ok({
11
+ // oxlint-disable-next-line require-await -- stub adapter satisfies async interface
12
+ authenticate: async () => Result.ok(null),
13
+ }),
14
+ description: 'Authentication adapter',
15
+ metadata: { category: 'infrastructure' },
16
+ mock: () => ({
17
+ // oxlint-disable-next-line require-await -- mock adapter satisfies async interface
18
+ authenticate: async () => Result.ok(null),
19
+ }),
20
+ });
21
+ //# sourceMappingURL=auth-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-service.js","sourceRoot":"","sources":["../src/auth-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIjD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAc,MAAM,EAAE;IACtD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CACf,MAAM,CAAC,EAAE,CAAC;QACR,mFAAmF;QACnF,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;KACpB,CAAC;IAC1B,WAAW,EAAE,wBAAwB;IACrC,QAAQ,EAAE,EAAE,QAAQ,EAAE,gBAAgB,EAAE;IACxC,IAAI,EAAE,GAAG,EAAE,CACT,CAAC;QACC,mFAAmF;QACnF,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;KAC1C,CAAuB;CAC3B,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { PermissionError } from '@ontrails/core';
2
+ /**
3
+ * Error returned when permit scope enforcement fails.
4
+ *
5
+ * Extends `PermissionError` (category `'permission'`, HTTP 403) because
6
+ * it represents an *authorization* failure — the caller's identity is known
7
+ * but lacks the required scopes.
8
+ */
9
+ export declare class PermitError extends PermissionError {
10
+ constructor(message: string, options?: {
11
+ cause?: Error;
12
+ context?: Record<string, unknown>;
13
+ });
14
+ }
15
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,qBAAa,WAAY,SAAQ,eAAe;gBAE5C,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE;CAKjE"}
package/dist/errors.js ADDED
@@ -0,0 +1,15 @@
1
+ import { PermissionError } from '@ontrails/core';
2
+ /**
3
+ * Error returned when permit scope enforcement fails.
4
+ *
5
+ * Extends `PermissionError` (category `'permission'`, HTTP 403) because
6
+ * it represents an *authorization* failure — the caller's identity is known
7
+ * but lacks the required scopes.
8
+ */
9
+ export class PermitError extends PermissionError {
10
+ constructor(message, options) {
11
+ super(message, options);
12
+ this.name = 'PermitError';
13
+ }
14
+ }
15
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,eAAe;IAC9C,YACE,OAAe,EACf,OAA8D;QAE9D,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Normalized input for auth adapters.
3
+ *
4
+ * Each surface extracts raw credentials from its transport and normalizes
5
+ * them into this shape. No surface types (Request, McpSession, etc.) cross
6
+ * into core — only this interface.
7
+ */
8
+ export interface PermitExtractionInput {
9
+ /** Which surface produced this extraction */
10
+ readonly surface: 'http' | 'mcp' | 'cli';
11
+ /** Bearer token from Authorization header or equivalent */
12
+ readonly bearerToken?: string;
13
+ /** Session identifier from transport handshake */
14
+ readonly sessionId?: string;
15
+ /** Raw headers (HTTP surface only, typically) */
16
+ readonly headers?: Headers;
17
+ /** Correlation ID for tracing */
18
+ readonly requestId: string;
19
+ }
20
+ //# sourceMappingURL=extraction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../src/extraction.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACzC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extraction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.js","sourceRoot":"","sources":["../src/extraction.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ export { type AuthAdapter, type AuthCredentials, type AuthError, } from './adapter.js';
2
+ export { createJwtAdapter, type JwtAdapterOptions } from './adapters/jwt.js';
3
+ export { authLayer } from './auth-layer.js';
4
+ export { authService } from './auth-service.js';
5
+ export { authVerify } from './trails/auth-verify.js';
6
+ export { PermitError } from './errors.js';
7
+ export { type PermitExtractionInput } from './extraction.js';
8
+ export { type Permit, getPermit } from './permit.js';
9
+ export { validatePermits, type PermitDiagnostic } from './rules.js';
10
+ export { mintTestPermit, mintPermitForTrail } from './testing.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export {} from './adapter.js';
2
+ export { createJwtAdapter } from './adapters/jwt.js';
3
+ export { authLayer } from './auth-layer.js';
4
+ export { authService } from './auth-service.js';
5
+ export { authVerify } from './trails/auth-verify.js';
6
+ export { PermitError } from './errors.js';
7
+ export {} from './extraction.js';
8
+ export { getPermit } from './permit.js';
9
+ export { validatePermits } from './rules.js';
10
+ export { mintTestPermit, mintPermitForTrail } from './testing.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAA0B,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAA8B,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAe,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,eAAe,EAAyB,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { BasePermit } from '@ontrails/core';
2
+ /** The resolved identity and scopes from a successful authentication. */
3
+ export interface Permit extends BasePermit {
4
+ readonly roles?: readonly string[];
5
+ readonly tenantId?: string;
6
+ readonly metadata?: Readonly<Record<string, unknown>>;
7
+ }
8
+ /**
9
+ * Type-safe accessor for `ctx.permit` with a downcast to `Permit`.
10
+ *
11
+ * `TrailContext.permit` is typed as `BasePermit` (id + scopes). This accessor
12
+ * returns the full `Permit` when the auth layer has set one. Safe because
13
+ * the auth layer is the only writer and always sets a full `Permit`.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const permit = getPermit(ctx);
18
+ * if (permit) {
19
+ * console.log(permit.roles, permit.tenantId);
20
+ * }
21
+ * ```
22
+ */
23
+ export declare const getPermit: (ctx: {
24
+ permit?: BasePermit;
25
+ }) => Permit | undefined;
26
+ //# sourceMappingURL=permit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permit.d.ts","sourceRoot":"","sources":["../src/permit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,yEAAyE;AACzE,MAAM,WAAW,MAAO,SAAQ,UAAU;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GAAI,KAAK;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,KAAG,MAAM,GAAG,SACH,CAAC"}
package/dist/permit.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Type-safe accessor for `ctx.permit` with a downcast to `Permit`.
3
+ *
4
+ * `TrailContext.permit` is typed as `BasePermit` (id + scopes). This accessor
5
+ * returns the full `Permit` when the auth layer has set one. Safe because
6
+ * the auth layer is the only writer and always sets a full `Permit`.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const permit = getPermit(ctx);
11
+ * if (permit) {
12
+ * console.log(permit.roles, permit.tenantId);
13
+ * }
14
+ * ```
15
+ */
16
+ export const getPermit = (ctx) => ctx.permit === undefined ? undefined : ctx.permit;
17
+ //# sourceMappingURL=permit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permit.js","sourceRoot":"","sources":["../src/permit.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAA4B,EAAsB,EAAE,CAC5E,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,GAAG,CAAC,MAAiB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { Trail } from '@ontrails/core';
2
+ /** A single governance finding from a permit rule. */
3
+ export interface PermitDiagnostic {
4
+ readonly trailId: string;
5
+ readonly rule: string;
6
+ readonly severity: 'error' | 'warning';
7
+ readonly message: string;
8
+ }
9
+ type AnyTrail = Trail<unknown, unknown>;
10
+ type Rule = (trails: readonly AnyTrail[]) => readonly PermitDiagnostic[];
11
+ /**
12
+ * Destructive trails without a real permit declaration are a governance failure.
13
+ *
14
+ * Reports an error for every trail with `intent: 'destroy'` that has no
15
+ * `permit` field or explicitly opts out with `permit: 'public'`.
16
+ */
17
+ export declare const destroyWithoutPermit: Rule;
18
+ /**
19
+ * Write trails without a permit declaration get a warning.
20
+ *
21
+ * Trails with `intent: 'write'` (or no intent, which defaults to write) that
22
+ * lack a permit are flagged unless `permit: 'public'` is explicitly set.
23
+ */
24
+ export declare const writeWithoutPermit: Rule;
25
+ /**
26
+ * Warns for scopes that don't follow the `entity:action` convention.
27
+ *
28
+ * A valid scope contains exactly one colon separating a non-empty entity
29
+ * and a non-empty action.
30
+ */
31
+ export declare const scopeNamingConsistency: Rule;
32
+ /**
33
+ * Warns for scopes that appear in only one trail's permit.
34
+ *
35
+ * Catches typos like `user:wirte` by surfacing scopes not shared with any
36
+ * other trail. Only runs when at least 2 trails have permit declarations.
37
+ */
38
+ export declare const orphanScopeDetection: Rule;
39
+ /**
40
+ * Run all permit governance rules against a set of trails.
41
+ *
42
+ * Returns a flat array of diagnostics from every rule. An empty array
43
+ * means the topo passes all permit governance checks.
44
+ */
45
+ export declare const validatePermits: (trails: readonly Trail<unknown, unknown>[]) => readonly PermitDiagnostic[];
46
+ export {};
47
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAM5C,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAMD,KAAK,QAAQ,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AACxC,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,KAAK,SAAS,gBAAgB,EAAE,CAAC;AAiBzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAU5B,CAAC;AAMR;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,IAU1B,CAAC;AAgBR;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,EAAE,IAUlC,CAAC;AA2CJ;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAMlC,CAAC;AAaF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,SAAS,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KACzC,SAAS,gBAAgB,EAA8C,CAAC"}