@fungi.computer/veil 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -84,6 +84,75 @@ Workerd's `startTls({ expectedServerHostname })` verifies the certificate
84
84
  against that name; it reports a verification failure on the first read, which
85
85
  the door types as `unavailable`.
86
86
 
87
+ ## Platform snapshot egress (private, not enabled by this change)
88
+
89
+ `configuredProviderEgress(env)` (from `./gatekeeper`) also owns the platform
90
+ snapshot routes below. Like the existing Hub search caller
91
+ (`apps/hub/src/agent-web-search.ts`, `configuredWebSearch`), a caller obtains
92
+ `baseUrl(route)` and passes credential-free requests through
93
+ `providerFetch(egress, route)`. Veil alone reads the bindings, pins the
94
+ destination and attaches the credential. The new holdings callers are composed
95
+ later; these routes neither mount Hub endpoints nor schedule jobs.
96
+
97
+ | Route | Destination / allowed calls | Private bindings |
98
+ | --------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
99
+ | `solana-rpc` | Helius mainnet, exactly `https://mainnet.helius-rpc.com/`, POST | `SOLANA_RPC_URL` (that credential-free root), `SOLANA_RPC_API_KEY` (Worker secret) |
100
+ | `solana-rpc-fallback` | One configured QuickNode Solana mainnet endpoint, exactly its HTTPS root, POST | `SOLANA_RPC_FALLBACK_URL` (`https://<endpoint>.solana-mainnet.quiknode.pro/`), `SOLANA_RPC_FALLBACK_API_KEY` (Worker secret) |
101
+ | `sanctions-screen` | GET exactly `https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/SDN.XML` | `SANCTIONS_SDN_URL` (that exact URL, opt-in) |
102
+
103
+ The RPC routes accept only single JSON-RPC 2.0 calls to `getAccountInfo`,
104
+ `getMultipleAccounts`, `getProgramAccounts`, `getTokenSupply` and `getSlot`:
105
+ mint/program-owner boot checks, token and lock account reads, supply and
106
+ finalized-slot reconciliation. Transactions, simulation, airdrops, arbitrary
107
+ methods and batches are refused. The holdings owner still owns finality, params,
108
+ maintained account decoders and reconciliation. QuickNode is the proposal's
109
+ second-provider choice, not an automatic retry: its caller must apply the same
110
+ snapshot checks on fallback.
111
+
112
+ Helius receives `?api-key=<secret>`; QuickNode receives `/<secret>/`. Neither
113
+ credential-bearing URL is returned from `baseUrl`. Caller headers, queries and
114
+ alternate paths are not forwarded. Keep keys in the existing private
115
+ Worker-secret custody, never Team storage, public config, telemetry or logs. The
116
+ final transport sees the authorized URL and must not log it. Missing/invalid
117
+ bindings refuse `unavailable` before fetch; other destinations or methods refuse
118
+ `denied`. RPC redirects and non-2xx responses refuse `unavailable`; no upstream
119
+ error body, headers or URL is returned. Successful bodies are bounded to 64 MiB
120
+ and refused on a credential echo (raw, JSON-escaped or percent-encoded); RPC
121
+ request bodies are bounded to 64 KiB. Transport failures, including aborts,
122
+ expose only fixed `VeilError` codes. The existing streaming model/search routes
123
+ and model-key rules are unchanged.
124
+
125
+ The sanctions GET alone may manually follow **one HTTP 302** from the exact OFAC
126
+ export to
127
+ `https://wc2h-sls-prod-public-published.s3.us-gov-west-1.amazonaws.com`, with a
128
+ path beginning `/Published/` and ending `/SDN.XML`. OFAC's signed query is
129
+ retained; caller headers/credentials and upstream cookies are not forwarded. The
130
+ Location string is capped at 8,192 code units, must have no userinfo or
131
+ fragment, and cannot change HTTPS or port 443. Veil cancels the redirect body
132
+ before the second GET, uses the original abort signal (including any caller
133
+ deadline, never restarted) and keeps the same 64 MiB download bound. Another
134
+ host/path, a missing/invalid Location, a second redirect or any other redirect
135
+ status refuses `unavailable`. Direct entry at S3 and non-GET sanctions requests
136
+ remain refused.
137
+
138
+ **Sanctions choice:** use the official OFAC SDN XML list, not the Chainalysis
139
+ free API. Read-only confirmation on 2026-10-31 found actual
140
+ `Digital Currency Address - SOL` identifiers in the official export (including
141
+ ID 194579), establishing Solana-address coverage without assuming a vendor's
142
+ chain support. OFAC's public list requires **no credential**, so there is no
143
+ `SANCTIONS_API_KEY` binding or invented anonymous-key fallback. F5 must parse
144
+ the XML through a maintained decoder, match the SOL identifiers, and fail closed
145
+ on an unavailable or unusable list; this door does not claim to screen persons,
146
+ countries or unlisted addresses.
147
+
148
+ Provider contract sources:
149
+ [Helius RPC](https://www.helius.dev/docs/rpc/overview),
150
+ [QuickNode endpoints](https://www.quicknode.com/docs/solana/endpoints),
151
+ [OFAC data formats](https://ofac.treasury.gov/specially-designated-nationals-list-data-formats-data-schemas)
152
+ and the exact official export above. Unit fixtures prove custody and route
153
+ policy, not provisioned keys, paid-plan large-snapshot support, live RPC
154
+ reconciliation or live screening; F4/F5 own those opt-in proofs.
155
+
87
156
  ## Model credentials and failure handling
88
157
 
89
158
  `src/model-credentials.ts` selects which key class may serve an inference
@@ -3,12 +3,11 @@
3
3
  * provider login policy: the local runtime, the TUI login registry and the Hub
4
4
  * read it; none of them decides visibility on its own.
5
5
  *
6
- * Stance: like Pi and OpenCode, offer a person's own subscription locally and
7
- * hosted, and turn a provider off through `subscriptionKillSwitch` if it
8
- * objects or blocks tokens. A subscription is always one person's: never
9
- * Team-scoped, shared or pooled. The new OpenAI ChatGPT plan flow is
10
- * local-only; hosted use awaits OpenAI approval. The retired Codex provider
11
- * is forbidden on both hosts.
6
+ * Offer a person's own Claude or ChatGPT subscription locally, never on
7
+ * hosted Fungi. A subscription is always one person's: never Team-scoped,
8
+ * shared or pooled. `subscriptionKillSwitch` can turn off a provider if it
9
+ * objects or blocks tokens. Hosted BYOK uses API keys. The retired Codex
10
+ * provider is forbidden on both hosts.
12
11
  * See wiki/3-resources/decisions/adr-byok-model-keys-v1.md.
13
12
  */
14
13
  /** The owner's own machine, or Fungi's servers acting for a Team. */
@@ -69,7 +68,9 @@ export declare const modelCredentialPolicy: Readonly<{
69
68
  status: "allowed";
70
69
  }>;
71
70
  hosted: Readonly<{
72
- status: "allowed";
71
+ status: "forbidden";
72
+ reason: string;
73
+ source: string;
73
74
  }>;
74
75
  };
75
76
  };
@@ -3,17 +3,21 @@
3
3
  * provider login policy: the local runtime, the TUI login registry and the Hub
4
4
  * read it; none of them decides visibility on its own.
5
5
  *
6
- * Stance: like Pi and OpenCode, offer a person's own subscription locally and
7
- * hosted, and turn a provider off through `subscriptionKillSwitch` if it
8
- * objects or blocks tokens. A subscription is always one person's: never
9
- * Team-scoped, shared or pooled. The new OpenAI ChatGPT plan flow is
10
- * local-only; hosted use awaits OpenAI approval. The retired Codex provider
11
- * is forbidden on both hosts.
6
+ * Offer a person's own Claude or ChatGPT subscription locally, never on
7
+ * hosted Fungi. A subscription is always one person's: never Team-scoped,
8
+ * shared or pooled. `subscriptionKillSwitch` can turn off a provider if it
9
+ * objects or blocks tokens. Hosted BYOK uses API keys. The retired Codex
10
+ * provider is forbidden on both hosts.
12
11
  * See wiki/3-resources/decisions/adr-byok-model-keys-v1.md.
13
12
  */
14
13
  const allowed = { status: "allowed" };
15
14
  const unreviewed = { status: "unreviewed" };
16
15
  const notOffered = {
16
+ status: "forbidden",
17
+ reason: "Fungi does not offer this credential method on this host",
18
+ source: "wiki/3-resources/decisions/adr-byok-model-keys-v1.md",
19
+ };
20
+ const providerNotOffered = {
17
21
  status: "forbidden",
18
22
  reason: "The provider does not offer this credential method",
19
23
  source: "provider documentation",
@@ -25,7 +29,7 @@ const retiredCodex = {
25
29
  };
26
30
  const apiKeyOnly = {
27
31
  api_key: { local: allowed, hosted: allowed },
28
- subscription: { local: notOffered, hosted: notOffered },
32
+ subscription: { local: providerNotOffered, hosted: providerNotOffered },
29
33
  };
30
34
  /**
31
35
  * Reviewed providers, keyed by Pi provider id. `openai-compatible` is a
@@ -38,9 +42,8 @@ export const modelCredentialPolicy = Object.freeze({
38
42
  },
39
43
  anthropic: {
40
44
  api_key: { local: allowed, hosted: allowed },
41
- // Local: Pi's Claude Pro/Max OAuth, as Pi and OpenCode ship it. Hosted: the
42
- // unmodified Claude Code binary on the owner's Computer, signed in by them.
43
- subscription: { local: allowed, hosted: allowed },
45
+ // Local: Pi's Claude Pro/Max OAuth. Hosted: no Claude subscription login.
46
+ subscription: { local: allowed, hosted: notOffered },
44
47
  },
45
48
  "openai-codex": {
46
49
  api_key: { local: retiredCodex, hosted: retiredCodex },
@@ -52,8 +55,8 @@ export const modelCredentialPolicy = Object.freeze({
52
55
  local: allowed,
53
56
  hosted: {
54
57
  status: "forbidden",
55
- reason: "ChatGPT plan usage in a hosted app requires OpenAI approval",
56
- source: "https://developers.openai.com/siwc/token-sharing-open-source",
58
+ reason: "Fungi does not offer ChatGPT subscription sign-in on hosted Fungi; approval alone would not enable it",
59
+ source: "wiki/3-resources/decisions/adr-byok-model-keys-v1.md",
57
60
  },
58
61
  },
59
62
  },
@@ -14,6 +14,9 @@
14
14
  * `/parallel` route, with `x-api-key`.
15
15
  * - `ai-gateway-logs`: Cloudflare's API log read used for usage
16
16
  * reconciliation, `Authorization: Bearer` with the gateway token.
17
+ * - `solana-rpc` / `solana-rpc-fallback`: finalized snapshot reads through
18
+ * Helius / QuickNode, with private query / path credentials.
19
+ * - `sanctions-screen`: the official OFAC SDN XML export, without a key.
17
20
  */
18
21
  /** Private Worker bindings read only here; never Team storage or responses. */
19
22
  export type ProviderKeyBindings = Readonly<{
@@ -24,8 +27,16 @@ export type ProviderKeyBindings = Readonly<{
24
27
  /** Cloudflare account that owns the gateway; routes are scoped to it. */
25
28
  AI_GATEWAY_ACCOUNT_ID?: string;
26
29
  AI_GATEWAY_ID?: string;
30
+ /** Credential-free Helius mainnet root; the key is a separate Worker secret. */
31
+ SOLANA_RPC_URL?: string;
32
+ SOLANA_RPC_API_KEY?: string;
33
+ /** Credential-free QuickNode Solana mainnet root; token is a Worker secret. */
34
+ SOLANA_RPC_FALLBACK_URL?: string;
35
+ SOLANA_RPC_FALLBACK_API_KEY?: string;
36
+ /** Opt-in to the pinned public OFAC SDN XML export. No API key is needed. */
37
+ SANCTIONS_SDN_URL?: string;
27
38
  }>;
28
- export type ProviderRoute = "camel" | "ai-gateway" | "parallel" | "ai-gateway-logs";
39
+ export type ProviderRoute = "camel" | "ai-gateway" | "parallel" | "ai-gateway-logs" | "solana-rpc" | "solana-rpc-fallback" | "sanctions-screen";
29
40
  /**
30
41
  * The provider-key door. `fetch` is the transport the authorized request is
31
42
  * finally sent on (a test double, or the Hub's telemetry-wrapped fetch).
@@ -33,16 +44,16 @@ export type ProviderRoute = "camel" | "ai-gateway" | "parallel" | "ai-gateway-lo
33
44
  export declare function createProviderEgress(env: ProviderKeyBindings, options?: Readonly<{
34
45
  fetch?: typeof globalThis.fetch;
35
46
  }>): {
36
- /** Whether this route's credential is configured; reveals no value. */
47
+ /** Whether required custody/configuration is present; reveals no value. */
37
48
  credentialed(route: ProviderRoute): boolean;
38
49
  /** The route's public base URL, or `null` while it is not configured. */
39
50
  baseUrl(route: ProviderRoute): string | null;
40
51
  /**
41
52
  * Send a credential-free request to a route. Refuses (`denied`) any
42
53
  * destination outside the route and (`unavailable`) a route with no
43
- * credential. Redirects are never followed, so a redirect cannot carry
44
- * the key to another origin (workerd has no `error` mode; `manual` hands
45
- * the 3xx back instead).
54
+ * credential. Credentialed routes never follow redirects (workerd has no
55
+ * `error` mode; `manual` hands the 3xx back instead). Only the anonymous
56
+ * sanctions GET may follow one pinned OFAC government-S3 302.
46
57
  */
47
58
  fetch(route: ProviderRoute, input: string | URL | Request, init?: RequestInit): Promise<Response>;
48
59
  };
@@ -1,8 +1,33 @@
1
+ import { z } from "zod";
1
2
  import { VeilError } from "./index.js";
2
3
  const CAMEL_BASE = "https://stream.camelai.com/v1";
3
4
  const PARALLEL_BASE = "https://api.parallel.ai";
4
5
  const GATEWAY_ORIGIN = "https://gateway.ai.cloudflare.com";
5
6
  const CLOUDFLARE_API_ORIGIN = "https://api.cloudflare.com";
7
+ const HELIUS_BASE = "https://mainnet.helius-rpc.com/";
8
+ const OFAC_SDN = "https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/SDN.XML";
9
+ const OFAC_DOWNLOAD_ORIGIN = "https://wc2h-sls-prod-public-published.s3.us-gov-west-1.amazonaws.com";
10
+ // Single read-only JSON-RPC calls used by the finalized holdings/lock reader.
11
+ // Zod owns JSON envelope decoding; the holdings owner owns params and results.
12
+ const snapshotCall = z
13
+ .object({
14
+ jsonrpc: z.literal("2.0"),
15
+ id: z.union([z.string(), z.number()]),
16
+ method: z.enum([
17
+ "getAccountInfo",
18
+ "getMultipleAccounts",
19
+ "getProgramAccounts",
20
+ "getTokenSupply",
21
+ "getSlot",
22
+ ]),
23
+ params: z.array(z.unknown()).optional(),
24
+ })
25
+ .strict();
26
+ function snapshotRoute(route) {
27
+ return (route === "solana-rpc" ||
28
+ route === "solana-rpc-fallback" ||
29
+ route === "sanctions-screen");
30
+ }
6
31
  const present = (value) => {
7
32
  const trimmed = value?.trim();
8
33
  return trimmed === undefined || trimmed === "" ? undefined : trimmed;
@@ -18,6 +43,34 @@ function gatewayScope(env) {
18
43
  /** The destination and credential for a route; `undefined` if either is missing. */
19
44
  function target(env, route) {
20
45
  switch (route) {
46
+ case "solana-rpc":
47
+ case "solana-rpc-fallback": {
48
+ const primary = route === "solana-rpc";
49
+ const raw = present(primary ? env.SOLANA_RPC_URL : env.SOLANA_RPC_FALLBACK_URL);
50
+ const credential = present(primary ? env.SOLANA_RPC_API_KEY : env.SOLANA_RPC_FALLBACK_API_KEY);
51
+ if (raw === undefined || credential === undefined)
52
+ return undefined;
53
+ if (!URL.canParse(raw))
54
+ return undefined;
55
+ const base = new URL(raw);
56
+ if (base.protocol !== "https:" ||
57
+ base.username ||
58
+ base.password ||
59
+ base.port ||
60
+ base.pathname !== "/" ||
61
+ base.search ||
62
+ base.hash)
63
+ return undefined;
64
+ if (primary
65
+ ? base.href !== HELIUS_BASE
66
+ : !/^[a-z0-9-]+\.solana-mainnet\.quiknode\.pro$/u.test(base.hostname))
67
+ return undefined;
68
+ return { base, credential };
69
+ }
70
+ case "sanctions-screen":
71
+ return present(env.SANCTIONS_SDN_URL) === OFAC_SDN
72
+ ? { base: new URL(OFAC_SDN) }
73
+ : undefined;
21
74
  case "camel": {
22
75
  const credential = present(env.CAMEL_API_KEY);
23
76
  return credential === undefined
@@ -67,6 +120,8 @@ function within(url, base) {
67
120
  }
68
121
  /** Replace whatever credential the caller sent with this route's own. */
69
122
  function authorize(route, headers, credential) {
123
+ if (credential === undefined)
124
+ throw new VeilError("unavailable");
70
125
  for (const name of [
71
126
  "authorization",
72
127
  "x-api-key",
@@ -75,6 +130,11 @@ function authorize(route, headers, credential) {
75
130
  ])
76
131
  headers.delete(name);
77
132
  switch (route) {
133
+ case "solana-rpc":
134
+ case "solana-rpc-fallback":
135
+ case "sanctions-screen":
136
+ // These doors construct their own headers and URL below.
137
+ return;
78
138
  case "camel":
79
139
  case "ai-gateway-logs":
80
140
  headers.set("authorization", `Bearer ${credential}`);
@@ -87,6 +147,130 @@ function authorize(route, headers, credential) {
87
147
  return;
88
148
  }
89
149
  }
150
+ /** A bounded body, before JSON decoding or returning a provider response. */
151
+ async function boundedText(message, limit) {
152
+ const reader = message.body?.getReader();
153
+ if (reader === undefined)
154
+ return "";
155
+ const chunks = [];
156
+ let size = 0;
157
+ try {
158
+ for (;;) {
159
+ const part = await reader.read();
160
+ if (part.done)
161
+ break;
162
+ const chunk = part.value;
163
+ if (!(chunk instanceof Uint8Array))
164
+ throw new VeilError("unavailable");
165
+ size += chunk.byteLength;
166
+ if (size > limit)
167
+ throw new VeilError("unavailable");
168
+ chunks.push(chunk);
169
+ }
170
+ return await new Blob(chunks).text();
171
+ }
172
+ finally {
173
+ try {
174
+ await reader.cancel();
175
+ }
176
+ finally {
177
+ reader.releaseLock();
178
+ }
179
+ }
180
+ }
181
+ /** Fixed, non-streaming snapshot doors never return upstream headers or URLs. */
182
+ async function fetchSnapshot(route, resolved, request, transport) {
183
+ try {
184
+ const url = new URL(request.url);
185
+ if (url.href !== resolved.base.href)
186
+ throw new VeilError("denied");
187
+ const screening = route === "sanctions-screen";
188
+ if (request.method !== (screening ? "GET" : "POST"))
189
+ throw new VeilError("denied");
190
+ let body;
191
+ if (!screening) {
192
+ body = await boundedText(request, 64 * 1024);
193
+ let envelope;
194
+ try {
195
+ envelope = JSON.parse(body);
196
+ }
197
+ catch {
198
+ throw new VeilError("denied");
199
+ }
200
+ if (!snapshotCall.safeParse(envelope).success)
201
+ throw new VeilError("denied");
202
+ }
203
+ const destination = new URL(resolved.base);
204
+ const key = resolved.credential;
205
+ if (!screening) {
206
+ if (key === undefined)
207
+ throw new VeilError("unavailable");
208
+ if (route === "solana-rpc")
209
+ destination.searchParams.set("api-key", key);
210
+ else
211
+ destination.pathname = `/${encodeURIComponent(key)}/`;
212
+ }
213
+ const init = {
214
+ method: request.method,
215
+ headers: screening
216
+ ? { accept: "application/xml" }
217
+ : {
218
+ accept: "application/json",
219
+ "content-type": "application/json",
220
+ },
221
+ signal: request.signal,
222
+ redirect: "manual",
223
+ };
224
+ if (body !== undefined)
225
+ init.body = body;
226
+ let response = await transport(new Request(destination, init));
227
+ // OFAC publishes a signed S3 download URL. Only this anonymous GET door
228
+ // may follow that one observed 302; the next response must succeed.
229
+ if (screening && response.status === 302) {
230
+ const location = response.headers.get("location");
231
+ await response.body?.cancel();
232
+ if (location === null ||
233
+ location.length > 8192 ||
234
+ !URL.canParse(location))
235
+ throw new VeilError("unavailable");
236
+ const download = new URL(location);
237
+ if (download.origin !== OFAC_DOWNLOAD_ORIGIN ||
238
+ download.username !== "" ||
239
+ download.password !== "" ||
240
+ !download.pathname.startsWith("/Published/") ||
241
+ !download.pathname.endsWith("/SDN.XML") ||
242
+ download.hash !== "")
243
+ throw new VeilError("unavailable");
244
+ request.signal.throwIfAborted();
245
+ response = await transport(new Request(download, {
246
+ method: "GET",
247
+ headers: { accept: "application/xml" },
248
+ signal: request.signal,
249
+ redirect: "manual",
250
+ }));
251
+ }
252
+ if (!response.ok) {
253
+ await response.body?.cancel();
254
+ throw new VeilError("unavailable");
255
+ }
256
+ const text = await boundedText(response, 64 * 1024 * 1024);
257
+ if (key !== undefined &&
258
+ [key, encodeURIComponent(key), JSON.stringify(key).slice(1, -1)].some((echo) => text.includes(echo)))
259
+ throw new VeilError("unavailable");
260
+ return new Response(text, {
261
+ status: response.status,
262
+ headers: {
263
+ "content-type": screening ? "application/xml" : "application/json",
264
+ },
265
+ });
266
+ }
267
+ catch (error) {
268
+ // Even aborted transports can include a credential-bearing URL in errors.
269
+ if (error instanceof VeilError)
270
+ throw error;
271
+ throw new VeilError("unavailable");
272
+ }
273
+ }
90
274
  /**
91
275
  * The provider-key door. `fetch` is the transport the authorized request is
92
276
  * finally sent on (a test double, or the Hub's telemetry-wrapped fetch).
@@ -94,9 +278,13 @@ function authorize(route, headers, credential) {
94
278
  export function createProviderEgress(env, options = {}) {
95
279
  const transport = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
96
280
  return {
97
- /** Whether this route's credential is configured; reveals no value. */
281
+ /** Whether required custody/configuration is present; reveals no value. */
98
282
  credentialed(route) {
99
283
  switch (route) {
284
+ case "solana-rpc":
285
+ case "solana-rpc-fallback":
286
+ case "sanctions-screen":
287
+ return target(env, route) !== undefined;
100
288
  case "camel":
101
289
  return present(env.CAMEL_API_KEY) !== undefined;
102
290
  case "parallel":
@@ -116,9 +304,9 @@ export function createProviderEgress(env, options = {}) {
116
304
  /**
117
305
  * Send a credential-free request to a route. Refuses (`denied`) any
118
306
  * destination outside the route and (`unavailable`) a route with no
119
- * credential. Redirects are never followed, so a redirect cannot carry
120
- * the key to another origin (workerd has no `error` mode; `manual` hands
121
- * the 3xx back instead).
307
+ * credential. Credentialed routes never follow redirects (workerd has no
308
+ * `error` mode; `manual` hands the 3xx back instead). Only the anonymous
309
+ * sanctions GET may follow one pinned OFAC government-S3 302.
122
310
  */
123
311
  async fetch(route, input, init) {
124
312
  const resolved = target(env, route);
@@ -134,6 +322,8 @@ export function createProviderEgress(env, options = {}) {
134
322
  }
135
323
  if (!within(new URL(request.url), resolved.base))
136
324
  throw new VeilError("denied");
325
+ if (snapshotRoute(route))
326
+ return fetchSnapshot(route, resolved, request, transport);
137
327
  const headers = new Headers(request.headers);
138
328
  authorize(route, headers, resolved.credential);
139
329
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/veil",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Team-authorized credential custody and provider access",
6
6
  "type": "module",