wicked-crew 0.5.0 → 0.6.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.
Files changed (116) hide show
  1. package/dist/api/audit.d.ts +57 -0
  2. package/dist/api/audit.d.ts.map +1 -0
  3. package/dist/api/audit.js +115 -0
  4. package/dist/api/audit.js.map +1 -0
  5. package/dist/api/auth.d.ts +192 -0
  6. package/dist/api/auth.d.ts.map +1 -0
  7. package/dist/api/auth.js +515 -0
  8. package/dist/api/auth.js.map +1 -0
  9. package/dist/api/gate-cache.d.ts +8 -0
  10. package/dist/api/gate-cache.d.ts.map +1 -1
  11. package/dist/api/gate-cache.js +10 -0
  12. package/dist/api/gate-cache.js.map +1 -1
  13. package/dist/api/open-path.d.ts +18 -0
  14. package/dist/api/open-path.d.ts.map +1 -0
  15. package/dist/api/open-path.js +79 -0
  16. package/dist/api/open-path.js.map +1 -0
  17. package/dist/api/routes.d.ts +112 -1
  18. package/dist/api/routes.d.ts.map +1 -1
  19. package/dist/api/routes.js +435 -16
  20. package/dist/api/routes.js.map +1 -1
  21. package/dist/api/seat-health.d.ts +55 -0
  22. package/dist/api/seat-health.d.ts.map +1 -0
  23. package/dist/api/seat-health.js +273 -0
  24. package/dist/api/seat-health.js.map +1 -0
  25. package/dist/api/seat-signin.d.ts +27 -0
  26. package/dist/api/seat-signin.d.ts.map +1 -0
  27. package/dist/api/seat-signin.js +143 -0
  28. package/dist/api/seat-signin.js.map +1 -0
  29. package/dist/api/server.d.ts +111 -3
  30. package/dist/api/server.d.ts.map +1 -1
  31. package/dist/api/server.js +236 -11
  32. package/dist/api/server.js.map +1 -1
  33. package/dist/cli/index.js +97 -3
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/cli/mcp.d.ts +14 -0
  36. package/dist/cli/mcp.d.ts.map +1 -0
  37. package/dist/cli/mcp.js +119 -0
  38. package/dist/cli/mcp.js.map +1 -0
  39. package/dist/core/adapter.d.ts +64 -1
  40. package/dist/core/adapter.d.ts.map +1 -1
  41. package/dist/core/adapter.js +162 -4
  42. package/dist/core/adapter.js.map +1 -1
  43. package/dist/core/types.d.ts +23 -361
  44. package/dist/core/types.d.ts.map +1 -1
  45. package/dist/core/types.js +11 -4
  46. package/dist/core/types.js.map +1 -1
  47. package/dist/interactive/bridge-pool.d.ts +71 -0
  48. package/dist/interactive/bridge-pool.d.ts.map +1 -0
  49. package/dist/interactive/bridge-pool.js +187 -0
  50. package/dist/interactive/bridge-pool.js.map +1 -0
  51. package/dist/interactive/bridge-root.d.ts +36 -0
  52. package/dist/interactive/bridge-root.d.ts.map +1 -0
  53. package/dist/interactive/bridge-root.js +48 -0
  54. package/dist/interactive/bridge-root.js.map +1 -0
  55. package/dist/interactive/draft-events.d.ts +144 -0
  56. package/dist/interactive/draft-events.d.ts.map +1 -0
  57. package/dist/interactive/draft-events.js +490 -0
  58. package/dist/interactive/draft-events.js.map +1 -0
  59. package/dist/interactive/edit-events.d.ts +172 -0
  60. package/dist/interactive/edit-events.d.ts.map +1 -0
  61. package/dist/interactive/edit-events.js +530 -0
  62. package/dist/interactive/edit-events.js.map +1 -0
  63. package/dist/interactive/ledger.d.ts +39 -0
  64. package/dist/interactive/ledger.d.ts.map +1 -0
  65. package/dist/interactive/ledger.js +93 -0
  66. package/dist/interactive/ledger.js.map +1 -0
  67. package/dist/interactive/proxy-routes.d.ts +39 -0
  68. package/dist/interactive/proxy-routes.d.ts.map +1 -0
  69. package/dist/interactive/proxy-routes.js +189 -0
  70. package/dist/interactive/proxy-routes.js.map +1 -0
  71. package/dist/interactive/ws-relay.d.ts +85 -0
  72. package/dist/interactive/ws-relay.d.ts.map +1 -0
  73. package/dist/interactive/ws-relay.js +191 -0
  74. package/dist/interactive/ws-relay.js.map +1 -0
  75. package/dist/projects/activity.d.ts +29 -0
  76. package/dist/projects/activity.d.ts.map +1 -0
  77. package/dist/projects/activity.js +172 -0
  78. package/dist/projects/activity.js.map +1 -0
  79. package/dist/projects/charter.d.ts +28 -0
  80. package/dist/projects/charter.d.ts.map +1 -0
  81. package/dist/projects/charter.js +53 -0
  82. package/dist/projects/charter.js.map +1 -0
  83. package/dist/projects/events.d.ts +55 -0
  84. package/dist/projects/events.d.ts.map +1 -0
  85. package/dist/projects/events.js +141 -0
  86. package/dist/projects/events.js.map +1 -0
  87. package/dist/projects/membership-index.d.ts +25 -0
  88. package/dist/projects/membership-index.d.ts.map +1 -0
  89. package/dist/projects/membership-index.js +46 -0
  90. package/dist/projects/membership-index.js.map +1 -0
  91. package/dist/projects/routes.d.ts +90 -0
  92. package/dist/projects/routes.d.ts.map +1 -0
  93. package/dist/projects/routes.js +388 -0
  94. package/dist/projects/routes.js.map +1 -0
  95. package/dist/projects/settings.d.ts +32 -0
  96. package/dist/projects/settings.d.ts.map +1 -0
  97. package/dist/projects/settings.js +64 -0
  98. package/dist/projects/settings.js.map +1 -0
  99. package/dist/qe/acceptance.d.ts +137 -0
  100. package/dist/qe/acceptance.d.ts.map +1 -0
  101. package/dist/qe/acceptance.js +249 -0
  102. package/dist/qe/acceptance.js.map +1 -0
  103. package/dist/qe/gate-events.d.ts +111 -0
  104. package/dist/qe/gate-events.d.ts.map +1 -0
  105. package/dist/qe/gate-events.js +168 -0
  106. package/dist/qe/gate-events.js.map +1 -0
  107. package/dist/qe/ledger.d.ts +100 -0
  108. package/dist/qe/ledger.d.ts.map +1 -0
  109. package/dist/qe/ledger.js +154 -0
  110. package/dist/qe/ledger.js.map +1 -0
  111. package/dist/studio/assets/index-CCwXa1cn.js +428 -0
  112. package/dist/studio/assets/index-HWxo0h41.css +32 -0
  113. package/dist/studio/index.html +2 -2
  114. package/package.json +10 -4
  115. package/dist/studio/assets/index-DaaUU8Ep.css +0 -32
  116. package/dist/studio/assets/index-Fu5DRC00.js +0 -423
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The crew-side actor audit trail (task #88).
3
+ *
4
+ * The engine's `LaunchOptions` carries no actor field, so WHO launched a run,
5
+ * WHO approved a gate, and WHO rewrote a policy is knowledge only the daemon's
6
+ * HTTP layer ever holds — this module is where it stops being ephemeral. One
7
+ * JSON line per privileged action, appended to `~/.wicked-crew/audit.log`
8
+ * (override: `WICKED_CREW_AUDIT_LOG`), read back by `GET /audit`.
9
+ *
10
+ * Posture: LOUD-NON-FATAL, matching the daemon's bus seams — an unwritable
11
+ * audit file must not turn a valid gate approval into a 500, but it must be
12
+ * SAID (once per failure) rather than swallowed. Appends are serialized on a
13
+ * promise chain so concurrent handlers never interleave partial lines.
14
+ */
15
+ import type { Actor, AuditEntry } from '../core/types.js';
16
+ /** Where the audit trail lives unless overridden. */
17
+ export declare function defaultAuditPath(env?: NodeJS.ProcessEnv): string;
18
+ export interface AuditReadFilter {
19
+ runId?: string;
20
+ action?: string;
21
+ /** Max entries returned (newest first). Default 200, capped at 1000. */
22
+ limit?: number;
23
+ }
24
+ export declare class AuditLog {
25
+ readonly path: string;
26
+ private readonly warn;
27
+ private chain;
28
+ private dirReady;
29
+ private disabled;
30
+ constructor(path?: string, warn?: (msg: string) => void);
31
+ /**
32
+ * A trail that records nothing and reads back empty — the DEFAULT for
33
+ * directly-driven route sets (unit tests call `registerRoutes` without a
34
+ * server), so they never write the operator's real `~/.wicked-crew/audit.log`
35
+ * or leave appends pending after close (Copilot, #250). `createServer`
36
+ * always builds a real one; this never rides a production path.
37
+ */
38
+ static noop(): AuditLog;
39
+ /**
40
+ * Record one action. Fire-and-forget by design (the route's answer must not
41
+ * wait on fsync of a sidecar), serialized so lines never interleave, loud on
42
+ * failure. `flush()` awaits the chain — tests and shutdown use it.
43
+ */
44
+ record(action: string, actor: Actor, fields?: {
45
+ runId?: string;
46
+ detail?: Record<string, unknown>;
47
+ }): void;
48
+ /** Await every append issued so far. */
49
+ flush(): Promise<void>;
50
+ /**
51
+ * Read the trail, newest first. A line that does not parse is skipped (a
52
+ * torn final line after a crash is expected once) — the rest of the trail
53
+ * still answers.
54
+ */
55
+ read(filter?: AuditReadFilter): Promise<AuditEntry[]>;
56
+ }
57
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1D,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE7E;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,QAAQ;IAMjB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAGd,IAAI,GAAE,MAA2B,EACzB,IAAI,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAA6B;IAGvE;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,IAAI,QAAQ;IAMvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,IAAI;IAyBzG,wCAAwC;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB;;;;OAIG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CA2B5D"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The crew-side actor audit trail (task #88).
3
+ *
4
+ * The engine's `LaunchOptions` carries no actor field, so WHO launched a run,
5
+ * WHO approved a gate, and WHO rewrote a policy is knowledge only the daemon's
6
+ * HTTP layer ever holds — this module is where it stops being ephemeral. One
7
+ * JSON line per privileged action, appended to `~/.wicked-crew/audit.log`
8
+ * (override: `WICKED_CREW_AUDIT_LOG`), read back by `GET /audit`.
9
+ *
10
+ * Posture: LOUD-NON-FATAL, matching the daemon's bus seams — an unwritable
11
+ * audit file must not turn a valid gate approval into a 500, but it must be
12
+ * SAID (once per failure) rather than swallowed. Appends are serialized on a
13
+ * promise chain so concurrent handlers never interleave partial lines.
14
+ */
15
+ import { appendFile, mkdir, readFile } from 'node:fs/promises';
16
+ import { homedir } from 'node:os';
17
+ import { dirname, join } from 'node:path';
18
+ /** Where the audit trail lives unless overridden. */
19
+ export function defaultAuditPath(env = process.env) {
20
+ return env['WICKED_CREW_AUDIT_LOG'] ?? join(homedir(), '.wicked-crew', 'audit.log');
21
+ }
22
+ export class AuditLog {
23
+ path;
24
+ warn;
25
+ chain = Promise.resolve();
26
+ dirReady = false;
27
+ disabled = false;
28
+ constructor(path = defaultAuditPath(), warn = (m) => console.warn(m)) {
29
+ this.path = path;
30
+ this.warn = warn;
31
+ }
32
+ /**
33
+ * A trail that records nothing and reads back empty — the DEFAULT for
34
+ * directly-driven route sets (unit tests call `registerRoutes` without a
35
+ * server), so they never write the operator's real `~/.wicked-crew/audit.log`
36
+ * or leave appends pending after close (Copilot, #250). `createServer`
37
+ * always builds a real one; this never rides a production path.
38
+ */
39
+ static noop() {
40
+ const log = new AuditLog();
41
+ log.disabled = true;
42
+ return log;
43
+ }
44
+ /**
45
+ * Record one action. Fire-and-forget by design (the route's answer must not
46
+ * wait on fsync of a sidecar), serialized so lines never interleave, loud on
47
+ * failure. `flush()` awaits the chain — tests and shutdown use it.
48
+ */
49
+ record(action, actor, fields) {
50
+ if (this.disabled)
51
+ return;
52
+ const entry = {
53
+ ts: Date.now(),
54
+ action,
55
+ actor,
56
+ ...(fields?.runId !== undefined ? { runId: fields.runId } : {}),
57
+ ...(fields?.detail !== undefined ? { detail: fields.detail } : {}),
58
+ };
59
+ this.chain = this.chain.then(async () => {
60
+ if (!this.dirReady) {
61
+ await mkdir(dirname(this.path), { recursive: true });
62
+ this.dirReady = true;
63
+ }
64
+ await appendFile(this.path, `${JSON.stringify(entry)}\n`, 'utf8');
65
+ });
66
+ this.chain = this.chain.catch((err) => {
67
+ this.warn(`[audit] FAILED to record ${action} by ${actor.id} at ${this.path}: ${err instanceof Error ? err.message : String(err)} — the action itself succeeded; the trail has a hole`);
68
+ });
69
+ }
70
+ /** Await every append issued so far. */
71
+ flush() {
72
+ return this.chain;
73
+ }
74
+ /**
75
+ * Read the trail, newest first. A line that does not parse is skipped (a
76
+ * torn final line after a crash is expected once) — the rest of the trail
77
+ * still answers.
78
+ */
79
+ async read(filter) {
80
+ if (this.disabled)
81
+ return [];
82
+ await this.flush();
83
+ let raw;
84
+ try {
85
+ raw = await readFile(this.path, 'utf8');
86
+ }
87
+ catch (err) {
88
+ if (err.code === 'ENOENT')
89
+ return []; // nothing recorded yet
90
+ throw err;
91
+ }
92
+ const entries = [];
93
+ for (const line of raw.split('\n')) {
94
+ if (line.trim() === '')
95
+ continue;
96
+ try {
97
+ const e = JSON.parse(line);
98
+ if (typeof e.ts !== 'number' || typeof e.action !== 'string')
99
+ continue;
100
+ if (filter?.runId !== undefined && e.runId !== filter.runId)
101
+ continue;
102
+ if (filter?.action !== undefined && e.action !== filter.action)
103
+ continue;
104
+ entries.push(e);
105
+ }
106
+ catch {
107
+ /* torn line — skip it, keep the trail readable */
108
+ }
109
+ }
110
+ entries.reverse(); // file order is append order; the API answers newest first
111
+ const limit = Math.min(Math.max(filter?.limit ?? 200, 1), 1000);
112
+ return entries.slice(0, limit);
113
+ }
114
+ }
115
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/api/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * The daemon's identity/actor seam (task #88, locked decision #6).
3
+ *
4
+ * LOCAL IS SACRED: the default deployment stays exactly what it was — a
5
+ * 127.0.0.1-bound daemon with loopback CORS and NO auth, where every request
6
+ * implicitly acts as the full-trust local operator. Nothing in this module
7
+ * runs a verifier, reads a token file, or changes a response in that mode; it
8
+ * only pins ONE actor shape onto the request so downstream code (audit trail,
9
+ * bus events, gate decisions) never branches on "was there auth".
10
+ *
11
+ * TEAM/HOSTED REQUIRES AUTH: `WICKED_RUNTIME=team` or `WICKED_CREW_AUTH=required`
12
+ * arms a bearer-token check on `/api/v1/*` and the `/ws` upgrade paths.
13
+ * Verification is pluggable behind {@link TokenVerifier}; v1 ships the static
14
+ * workload-token verifier (hashed-at-rest file, {@link loadTokenFile}) and the
15
+ * OIDC verifier SEAM ({@link OidcConfig} + {@link createOidcVerifier}), whose
16
+ * implementation is a named follow-up — configuring OIDC today fails the boot
17
+ * LOUDLY rather than pretending an IdP was consulted.
18
+ *
19
+ * Deny semantics: missing/unknown token in required mode → 401 (with
20
+ * `WWW-Authenticate: Bearer`); an authenticated actor below a route's rung on
21
+ * the trust ladder → 403. The ladder is {@link requiredTrust} — minimal and
22
+ * documented (docs/auth.md), not a per-route registry.
23
+ */
24
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
25
+ import type { Actor, TrustLevel } from '../core/types.js';
26
+ declare module 'fastify' {
27
+ interface FastifyRequest {
28
+ /**
29
+ * The authenticated actor — or {@link LOCAL_ACTOR} when auth is off. Set by
30
+ * the identity hook for every request under `/api/v1` and `/ws`; routes
31
+ * outside that scope (SPA assets) never read it.
32
+ */
33
+ actor: Actor;
34
+ }
35
+ }
36
+ export type AuthMode = 'required' | 'off';
37
+ /**
38
+ * The implicit full-trust local operator. `admin` is the ladder's top rung —
39
+ * the local single-operator deployment has exactly one human and no boundary
40
+ * to enforce between that human and the daemon, so "full trust" and "admin"
41
+ * are the same fact spelled once.
42
+ */
43
+ export declare const LOCAL_ACTOR: Actor;
44
+ /** True when `actor` sits at or above `min` on the ladder. */
45
+ export declare function trustAtLeast(actor: Actor, min: TrustLevel): boolean;
46
+ /**
47
+ * Resolve the daemon's auth mode. DENY-DOMINANT: `WICKED_RUNTIME=team` forces
48
+ * `required` and nothing can talk it back down — a team runtime with auth
49
+ * switched off is exactly the silently-ungoverned state this seam exists to
50
+ * prevent. Outside team runtime, `WICKED_CREW_AUTH` (`required`|`off`) is
51
+ * explicit; unset means the local default, off.
52
+ */
53
+ export declare function resolveAuthMode(env?: NodeJS.ProcessEnv): AuthMode;
54
+ /**
55
+ * The verifier seam. A verifier resolves a presented bearer token to an
56
+ * {@link Actor}, or `null` when the token is not one of its. Verifiers are
57
+ * tried in order; the first non-null answer wins. v1 ships the static
58
+ * workload-token verifier; the OIDC verifier is the named follow-up
59
+ * (docs/auth.md § "The OIDC seam").
60
+ */
61
+ export interface TokenVerifier {
62
+ /** Names the verifier in logs and errors (`static-tokens`, `oidc`). */
63
+ readonly name: string;
64
+ verify(token: string): Promise<Actor | null>;
65
+ }
66
+ /** Where the workload-token file lives unless overridden. */
67
+ export declare function defaultTokensPath(env?: NodeJS.ProcessEnv): string;
68
+ /** One entry of `tokens.json` — the HASH of a token, never the token itself. */
69
+ export interface TokenFileEntry {
70
+ /** Lowercase hex SHA-256 of the bearer token (64 chars). */
71
+ sha256: string;
72
+ actor: Actor;
73
+ /** Operator note ("ci runner", "maria's laptop"); never used for decisions. */
74
+ label?: string;
75
+ }
76
+ /** `sha256` hex of a presented token — the only form the daemon stores or compares. */
77
+ export declare function tokenHash(token: string): string;
78
+ /**
79
+ * Parse a tokens.json payload into a hash→actor map. FAILS CLOSED on any
80
+ * malformed entry: a typo'd trust level that silently skipped its entry would
81
+ * lock an operator out (or, worse, a hand-edit that half-parsed would demote
82
+ * someone) with nothing saying so. The error names the entry index and field.
83
+ */
84
+ export declare function parseTokenFile(json: string, source: string): Map<string, Actor>;
85
+ /**
86
+ * Load `tokens.json` from disk into a static verifier. A MISSING file is a
87
+ * loud warning, not a boot failure: the daemon still starts and every request
88
+ * 401s, which is the honest fail-closed posture (an unreachable daemon tells
89
+ * the operator less than one that answers "no tokens are registered").
90
+ * A PRESENT-but-malformed file fails the boot — see {@link parseTokenFile}.
91
+ */
92
+ export declare function loadTokenFile(path: string, warn: (msg: string) => void): Map<string, Actor>;
93
+ /** The static workload-token verifier: sha256 the presented token, look it up. */
94
+ export declare function createStaticVerifier(entries: Map<string, Actor>): TokenVerifier;
95
+ /**
96
+ * The OIDC verifier's config shape. Lives in `~/.config/wicked-crew/auth.json`
97
+ * as `{ "oidc": … }`.
98
+ */
99
+ export interface OidcConfig {
100
+ /** The issuer URL — discovery happens at `<issuer>/.well-known/openid-configuration`. */
101
+ issuer: string;
102
+ /** The audience (`aud`) access tokens must carry to be accepted by this daemon. */
103
+ audience: string;
104
+ /** Explicit JWKS URI; omit to use the discovery document's `jwks_uri`. */
105
+ jwksUri?: string;
106
+ /**
107
+ * Claim mapping. `id` defaults to `sub`; `trust` names a claim whose value is
108
+ * a {@link TrustLevel} (defaults to the `defaultTrust` below when absent).
109
+ * OIDC-verified principals are always `kind: 'human'` — workloads use tokens.
110
+ */
111
+ claims?: {
112
+ id?: string;
113
+ trust?: string;
114
+ };
115
+ /** Trust assigned when the trust claim is absent (default `observer` — least privilege). */
116
+ defaultTrust?: TrustLevel;
117
+ }
118
+ /** Where the auth config (OIDC seam) lives unless overridden. */
119
+ export declare function defaultAuthConfigPath(env?: NodeJS.ProcessEnv): string;
120
+ /**
121
+ * Create an OIDC verifier that validates JWTs against the issuer's JWKS.
122
+ * Supports RS256/384/512, ES256/384/512, PS256/384/512. JWKS keys are cached
123
+ * per issuer with a 5-minute TTL and auto-refreshed on unknown kid.
124
+ */
125
+ export declare function createOidcVerifier(config: OidcConfig): TokenVerifier;
126
+ /** `createServer`'s auth options — everything defaults to env/file resolution. */
127
+ export interface AuthOptions {
128
+ /** Override mode resolution (tests / embedders). Default: {@link resolveAuthMode}. */
129
+ mode?: AuthMode;
130
+ /** Token file path. Default: `WICKED_CREW_TOKENS` or `~/.config/wicked-crew/tokens.json`. */
131
+ tokensPath?: string;
132
+ /** auth.json path (the OIDC seam's config). Default: `~/.config/wicked-crew/auth.json`. */
133
+ configPath?: string;
134
+ /**
135
+ * Replace the default verifier chain entirely (tests / embedders with their
136
+ * own identity source). When set, the token file and auth.json are not read.
137
+ */
138
+ verifiers?: TokenVerifier[];
139
+ /**
140
+ * Origins allowed CORS when auth is required, beyond loopback. Default:
141
+ * `WICKED_CREW_ALLOWED_ORIGINS` (comma-separated) or — when unset — ANY
142
+ * origin: bearer auth carries no ambient credential, so cross-origin JS
143
+ * without a token holds nothing (docs/auth.md § "CORS pairing").
144
+ */
145
+ allowedOrigins?: string[];
146
+ }
147
+ /** The resolved auth state `createServer` builds once and hooks consult per request. */
148
+ export interface ResolvedAuth {
149
+ mode: AuthMode;
150
+ verifiers: TokenVerifier[];
151
+ /** `null` = any origin (required mode's default); else an allowlist. Loopback always passes. */
152
+ allowedOrigins: string[] | null;
153
+ }
154
+ /** Resolve mode + verifier chain. Reads files ONLY in required mode. */
155
+ export declare function resolveAuth(options: AuthOptions | undefined, warn: (msg: string) => void): ResolvedAuth;
156
+ /**
157
+ * Minimum trust for a request. Minimal by design (the task's rule: no
158
+ * deny-by-default registry over unknown routes):
159
+ *
160
+ * - `/ws/terminals` and any sub-path → `operator` (write-capable WS channel;
161
+ * inbound frames are raw PTY stdin regardless of the GET upgrade method)
162
+ * - reads (GET/HEAD) and the main `/ws` stream → `observer`
163
+ * - governance writes, settings writes, project archive/restore → `admin`
164
+ * - every other mutation (launch, gate, cancel, chats, terminals, repos,
165
+ * workflows, project CRUD/membership) → `operator`
166
+ *
167
+ * `body` is consulted for exactly one rule: `PATCH /projects/:id` is an
168
+ * `operator` rename/describe unless the patch carries `status` (archive or
169
+ * restore), which changes what the whole team can attach work to — `admin`.
170
+ */
171
+ export declare function requiredTrust(method: string, path: string, body: unknown): TrustLevel;
172
+ /** True for the paths the auth boundary covers: the API and the WS upgrades. */
173
+ export declare function isProtectedPath(path: string): boolean;
174
+ /**
175
+ * Extract the presented bearer token. `Authorization: Bearer <t>` everywhere;
176
+ * the `/ws` upgrade paths ALSO accept `?access_token=` (RFC 6750 §2.3),
177
+ * because the browser `WebSocket` constructor cannot set headers. Header wins
178
+ * when both are present.
179
+ */
180
+ export declare function extractToken(req: FastifyRequest, path: string): string | null;
181
+ /**
182
+ * Install the identity (401) and trust (403) hooks.
183
+ *
184
+ * Identity runs at `onRequest` — before body parsing, and (per
185
+ * `@fastify/websocket`) before the upgrade completes, so a bad token is an
186
+ * HTTP 401 on the wire for REST and WS alike. Trust runs at `preHandler`,
187
+ * where the parsed body exists (the one body-dependent rule is the project
188
+ * archive). Both scope to {@link isProtectedPath}; the SPA shell and its
189
+ * assets stay public so a hosted skin can load and ASK for a token.
190
+ */
191
+ export declare function registerAuthHooks(app: FastifyInstance, auth: ResolvedAuth): void;
192
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAgB,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,KAAK,EAAa,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGrE,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB;;;;WAIG;QACH,KAAK,EAAE,KAAK,CAAC;KACd;CACF;AAED,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,KAIf,CAAC;AAMZ,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,QAAQ,CAS9E;AAID;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE9E;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uFAAuF;AACvF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAqC/E;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CA2B3F;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,aAAa,CAK/E;AAID;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,4FAA4F;IAC5F,YAAY,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAElF;AAgLD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CA8BpE;AAID,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,sFAAsF;IACtF,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,gGAAgG;IAChG,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACjC;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,CA+CvG;AAOD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAkBrF;AASD,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOrD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa7E;AASD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAsChF"}