@fungi.computer/veil 0.0.0-stage → 0.0.1

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -2
  3. package/dist/best-effort-cleanup.d.ts +2 -0
  4. package/dist/best-effort-cleanup.js +5 -0
  5. package/dist/event-verifiers.d.ts +80 -0
  6. package/dist/event-verifiers.js +168 -0
  7. package/dist/gatekeeper.d.ts +202 -0
  8. package/dist/gatekeeper.js +231 -0
  9. package/dist/index.d.ts +254 -0
  10. package/dist/index.js +267 -0
  11. package/dist/model-credentials.d.ts +226 -0
  12. package/dist/model-credentials.js +133 -0
  13. package/dist/model-key-grants.d.ts +56 -0
  14. package/dist/model-key-grants.js +107 -0
  15. package/dist/model-key-network.d.ts +33 -0
  16. package/dist/model-key-network.js +202 -0
  17. package/dist/model-keys.d.ts +42 -0
  18. package/dist/model-keys.js +115 -0
  19. package/dist/nango-custody-http.d.ts +16 -0
  20. package/dist/nango-custody-http.js +93 -0
  21. package/dist/nango-git.d.ts +19 -0
  22. package/dist/nango-git.js +125 -0
  23. package/dist/nango-model-keys.d.ts +13 -0
  24. package/dist/nango-model-keys.js +48 -0
  25. package/dist/nango-world-seat.d.ts +14 -0
  26. package/dist/nango-world-seat.js +47 -0
  27. package/dist/nango.d.ts +16 -0
  28. package/dist/nango.js +300 -0
  29. package/dist/openapi-contract.d.ts +64 -0
  30. package/dist/openapi-contract.js +11 -0
  31. package/dist/openapi-effect.d.ts +9 -0
  32. package/dist/openapi-effect.js +284 -0
  33. package/dist/openapi-http.d.ts +2 -0
  34. package/dist/openapi-http.js +37 -0
  35. package/dist/openapi-secrets.d.ts +6 -0
  36. package/dist/openapi-secrets.js +80 -0
  37. package/dist/openapi.d.ts +5 -0
  38. package/dist/openapi.js +24 -0
  39. package/dist/owned-infrastructure.d.ts +13 -0
  40. package/dist/owned-infrastructure.js +17 -0
  41. package/dist/provider-egress.d.ts +51 -0
  42. package/dist/provider-egress.js +155 -0
  43. package/dist/public-egress-policy.d.ts +13 -0
  44. package/dist/public-egress-policy.js +101 -0
  45. package/dist/public-fetch.d.ts +90 -0
  46. package/dist/public-fetch.js +347 -0
  47. package/dist/webhook-trigger-secrets.d.ts +23 -0
  48. package/dist/webhook-trigger-secrets.js +53 -0
  49. package/dist/world-seat.d.ts +40 -0
  50. package/dist/world-seat.js +82 -0
  51. package/package.json +140 -3
@@ -0,0 +1,231 @@
1
+ import { createOpenApiDoor, } from "./openapi.js";
2
+ import { createOpenApiEffects } from "./openapi-effect.js";
3
+ import { createDnsOverHttpsResolver } from "./model-key-network.js";
4
+ import { createNangoCustodian } from "./nango.js";
5
+ import { createWebhookTriggerSecrets } from "./webhook-trigger-secrets.js";
6
+ import { createNangoGitCustodian } from "./nango-git.js";
7
+ import { createNangoModelKeyCustodian } from "./nango-model-keys.js";
8
+ import { createNangoWorldSeatCustodian } from "./nango-world-seat.js";
9
+ import { worldSeatEndpoint } from "./world-seat.js";
10
+ import { createModelKeyEgress, createModelKeyGrants, } from "./model-key-grants.js";
11
+ import { VeilError } from "./index.js";
12
+ import { createProviderEgress, providerFetch, } from "./provider-egress.js";
13
+ import * as Schema from "effect/Schema";
14
+ import * as Exit from "effect/Exit";
15
+ /**
16
+ * The Gatekeeper: the one place the Hub's Nango key, webhook signing key,
17
+ * provider/model-key integration ids, and platform provider keys (Camel, the
18
+ * AI Gateway token, Parallel) are read out of the Worker environment and
19
+ * turned into a custodian or egress. These `configured*` functions are the custody
20
+ * boundary (`V1`): the Hub hands them its own `env` object opaquely and gets
21
+ * back an opaque custodian/egress handle, never the secret values themselves.
22
+ * No file outside `packages/veil` may read `env.NANGO_*` or
23
+ * `env.WEBHOOK_TRIGGER_MASTER_KEY`, `env.CAMEL_API_KEY`,
24
+ * `env.AI_GATEWAY_AUTH_TOKEN` or `env.PARALLEL_API_KEY` directly; the static
25
+ * boundary test `apps/hub/test/veil-custody-boundary.test.ts` enforces it.
26
+ * Hub code reaches a provider only through `configuredProviderEgress`.
27
+ */
28
+ export { providerFetch };
29
+ export function configuredGitHubState(env) {
30
+ const metadataIntegrationId = env.NANGO_GITHUB_INTEGRATION_ID;
31
+ const gitIntegrationId = env.NANGO_GITHUB_GIT_INTEGRATION_ID;
32
+ if (!metadataIntegrationId ||
33
+ !gitIntegrationId ||
34
+ metadataIntegrationId === gitIntegrationId)
35
+ return { github: "off" };
36
+ return {
37
+ github: "on",
38
+ metadataIntegrationId,
39
+ gitIntegrationId,
40
+ };
41
+ }
42
+ /**
43
+ * `veil.fetch` for platform provider keys. The Hub names a route and sends a
44
+ * credential-free request; Veil checks the destination and attaches the key.
45
+ * `fetch` is only the final transport (telemetry, or a test double).
46
+ */
47
+ export function configuredProviderEgress(env, options = {}) {
48
+ return createProviderEgress(env, options);
49
+ }
50
+ /** The Worker secret is the only custody root for per-trigger signing bytes. */
51
+ export function configuredWebhookTriggerSecrets(env) {
52
+ if (!env.WEBHOOK_TRIGGER_MASTER_KEY)
53
+ return { kind: "unavailable" };
54
+ try {
55
+ return {
56
+ kind: "ready",
57
+ custodian: createWebhookTriggerSecrets(env.WEBHOOK_TRIGGER_MASTER_KEY),
58
+ };
59
+ }
60
+ catch (cause) {
61
+ return { kind: "unavailable", cause };
62
+ }
63
+ }
64
+ /** Model API keys use their own integrations; none are required to boot. */
65
+ export function configuredModelKeyCustodian(env) {
66
+ if (!env.NANGO_SERVER_URL || !env.NANGO_API_KEY)
67
+ throw new VeilError("unavailable");
68
+ const integrations = {};
69
+ if (env.NANGO_ANTHROPIC_KEY_INTEGRATION_ID)
70
+ integrations.anthropic = env.NANGO_ANTHROPIC_KEY_INTEGRATION_ID;
71
+ if (env.NANGO_OPENAI_KEY_INTEGRATION_ID)
72
+ integrations.openai = env.NANGO_OPENAI_KEY_INTEGRATION_ID;
73
+ if (env.NANGO_OPENROUTER_KEY_INTEGRATION_ID)
74
+ integrations.openrouter = env.NANGO_OPENROUTER_KEY_INTEGRATION_ID;
75
+ if (env.NANGO_OPENAI_COMPATIBLE_KEY_INTEGRATION_ID)
76
+ integrations["openai-compatible"] =
77
+ env.NANGO_OPENAI_COMPATIBLE_KEY_INTEGRATION_ID;
78
+ return createNangoModelKeyCustodian({
79
+ serverUrl: env.NANGO_SERVER_URL,
80
+ apiKey: env.NANGO_API_KEY,
81
+ integrations,
82
+ });
83
+ }
84
+ /**
85
+ * Model-key grants are sealed under a key derived from the custodian's own
86
+ * Nango credential, so only a holder of custody can issue or open one.
87
+ */
88
+ export function configuredModelKeyGrants(env) {
89
+ if (!env.NANGO_API_KEY)
90
+ throw new VeilError("unavailable");
91
+ return createModelKeyGrants({ custodySecret: env.NANGO_API_KEY });
92
+ }
93
+ /** World-seat tokens use their own integration; it is not required to boot. */
94
+ export function configuredWorldSeatCustodian(env) {
95
+ if (!env.NANGO_SERVER_URL ||
96
+ !env.NANGO_API_KEY ||
97
+ !env.NANGO_WORLD_INTEGRATION_ID)
98
+ throw new VeilError("unavailable");
99
+ return createNangoWorldSeatCustodian({
100
+ serverUrl: env.NANGO_SERVER_URL,
101
+ apiKey: env.NANGO_API_KEY,
102
+ integrationId: env.NANGO_WORLD_INTEGRATION_ID,
103
+ allowLocalDev: env.HUB_LOCAL_MODE === "enabled",
104
+ });
105
+ }
106
+ /** The seat endpoint rule for this host: public HTTPS, or loopback when local. */
107
+ export function configuredWorldSeatEndpoint(env) {
108
+ const allowLocalDev = env.HUB_LOCAL_MODE === "enabled";
109
+ return (endpoint) => worldSeatEndpoint(endpoint, { allowLocalDev });
110
+ }
111
+ /** Final model egress a runner reaches with a delivered grant, never via TeamDO. */
112
+ export function configuredModelKeyEgress(env, options) {
113
+ return createModelKeyEgress({
114
+ grants: configuredModelKeyGrants(env),
115
+ custodian: () => configuredModelKeyCustodian(env),
116
+ ownerIsMember: options.ownerIsMember,
117
+ });
118
+ }
119
+ export function configuredVeilCustodian(env) {
120
+ const state = configuredGitHubState(env);
121
+ if (state.github === "off")
122
+ throw new VeilError("github_not_configured");
123
+ return configuredCustodian(env, state.metadataIntegrationId);
124
+ }
125
+ /** Git Connect/cleanup and final egress use the same Git-profile integration.
126
+ * No metadata Connection locator is translated into this profile. Veil confines
127
+ * the issued Connect document to the configured public Connect URLs. */
128
+ export function configuredComputerGitCustodian(env) {
129
+ const state = configuredGitHubState(env);
130
+ if (state.github === "off")
131
+ throw new VeilError("github_not_configured");
132
+ const custody = configuredCustodian(env, state.gitIntegrationId);
133
+ if (!env.NANGO_SERVER_URL || !env.NANGO_API_KEY)
134
+ throw new VeilError("unavailable");
135
+ try {
136
+ const git = createNangoGitCustodian({
137
+ serverUrl: env.NANGO_SERVER_URL,
138
+ apiKey: env.NANGO_API_KEY,
139
+ integrationId: state.gitIntegrationId,
140
+ });
141
+ return {
142
+ integrationId: state.gitIntegrationId,
143
+ openSession: custody.openSession,
144
+ verifyCompletion: custody.verifyCompletion,
145
+ verifyWebhook: custody.verifyWebhook,
146
+ findCompletion: custody.findCompletion,
147
+ deleteConnection: custody.deleteConnection,
148
+ readGit: git.readGit.bind(git),
149
+ };
150
+ }
151
+ catch {
152
+ throw new VeilError("unavailable");
153
+ }
154
+ }
155
+ /** The decoded key selects one of the two configured profiles only. The
156
+ * selected maintained custodian still verifies the original signed body. */
157
+ export async function verifyVeilWebhook(env, rawBody, signature) {
158
+ const state = configuredGitHubState(env);
159
+ if (state.github === "off")
160
+ throw new VeilError("github_not_configured");
161
+ const decoded = Schema.decodeUnknownExit(Schema.fromJsonString(Schema.Struct({
162
+ providerConfigKey: Schema.NonEmptyString,
163
+ })))(rawBody);
164
+ if (Exit.isFailure(decoded))
165
+ throw new VeilError("invalid_completion");
166
+ if (decoded.value.providerConfigKey === state.gitIntegrationId) {
167
+ return {
168
+ profile: "computer-git",
169
+ ...(await configuredComputerGitCustodian(env).verifyWebhook(rawBody, signature)),
170
+ };
171
+ }
172
+ if (decoded.value.providerConfigKey === state.metadataIntegrationId) {
173
+ return {
174
+ profile: "agent-metadata",
175
+ ...(await configuredVeilCustodian(env).verifyWebhook(rawBody, signature)),
176
+ };
177
+ }
178
+ throw new VeilError("invalid_completion");
179
+ }
180
+ function configuredCustodian(env, integrationId) {
181
+ if (!env.NANGO_SERVER_URL ||
182
+ !env.NANGO_API_KEY ||
183
+ !env.NANGO_WEBHOOK_SIGNING_KEY ||
184
+ !env.NANGO_CONNECT_BASE_URL ||
185
+ !env.NANGO_PUBLIC_API_URL ||
186
+ !integrationId)
187
+ throw new VeilError("unavailable");
188
+ try {
189
+ return createNangoCustodian({
190
+ serverUrl: env.NANGO_SERVER_URL,
191
+ apiKey: env.NANGO_API_KEY,
192
+ webhookSigningKey: env.NANGO_WEBHOOK_SIGNING_KEY,
193
+ integrationId,
194
+ connectBaseUrl: env.NANGO_CONNECT_BASE_URL,
195
+ publicApiUrl: env.NANGO_PUBLIC_API_URL,
196
+ });
197
+ }
198
+ catch {
199
+ throw new VeilError("unavailable");
200
+ }
201
+ }
202
+ /** Gatekeeper owns the common opaque configuration for both runtime facades. */
203
+ function openApiHostConfiguration(env, options) {
204
+ const doorOptions = {
205
+ connect: options.connect,
206
+ resolve: createDnsOverHttpsResolver(options.fetch),
207
+ networkPolicy: options.networkPolicy ?? {},
208
+ };
209
+ if (options.oauthProviders)
210
+ doorOptions.oauthProviders = options.oauthProviders;
211
+ if (env.OPENAPI_CREDENTIAL_MASTER_KEY)
212
+ doorOptions.custody = {
213
+ keys: {
214
+ activeKeyId: env.OPENAPI_CREDENTIAL_KEY_ID ?? "primary",
215
+ keys: {
216
+ ...options.previousCredentialKeys,
217
+ [env.OPENAPI_CREDENTIAL_KEY_ID ?? "primary"]: env.OPENAPI_CREDENTIAL_MASTER_KEY,
218
+ },
219
+ },
220
+ store: options.store,
221
+ };
222
+ return doorOptions;
223
+ }
224
+ /** Anonymous calls need no secret; API-key/bearer calls require configured Veil custody. */
225
+ export function configuredOpenApiDoor(env, options) {
226
+ return createOpenApiDoor(openApiHostConfiguration(env, options));
227
+ }
228
+ /** Owned composition retains canonical Effects; secrets are read only by Gatekeeper. */
229
+ export function configuredOpenApiEffects(env, options) {
230
+ return createOpenApiEffects(openApiHostConfiguration(env, options));
231
+ }
@@ -0,0 +1,254 @@
1
+ import * as Schema from "effect/Schema";
2
+ /** The repository identity selected by the Team, never by model input. */
3
+ export declare const RepositoryScopeSchema: Schema.Struct<{
4
+ readonly id: Schema.Number;
5
+ readonly owner: Schema.NonEmptyString;
6
+ readonly name: Schema.NonEmptyString;
7
+ }>;
8
+ export type RepositoryScope = Schema.Schema.Type<typeof RepositoryScopeSchema>;
9
+ /** Bounded, credential-free metadata returned by the GitHub operation. */
10
+ export declare const GitHubRepositorySchema: Schema.Struct<{
11
+ readonly id: Schema.Number;
12
+ readonly name: Schema.NonEmptyString;
13
+ readonly fullName: Schema.NonEmptyString;
14
+ readonly private: Schema.Boolean;
15
+ readonly defaultBranch: Schema.NonEmptyString;
16
+ }>;
17
+ export type GitHubRepository = Schema.Schema.Type<typeof GitHubRepositorySchema>;
18
+ export declare const ConnectInputSchema: Schema.Struct<{
19
+ readonly requestId: Schema.NonEmptyString;
20
+ readonly actorUserId: Schema.NonEmptyString;
21
+ readonly holderAgentId: Schema.NonEmptyString;
22
+ readonly repository: Schema.Struct<{
23
+ readonly id: Schema.Number;
24
+ readonly owner: Schema.NonEmptyString;
25
+ readonly name: Schema.NonEmptyString;
26
+ }>;
27
+ }>;
28
+ export type ConnectInput = Schema.Schema.Type<typeof ConnectInputSchema>;
29
+ export declare const AttemptSchema: Schema.Struct<{
30
+ readonly teamId: Schema.NonEmptyString;
31
+ readonly attemptId: Schema.NonEmptyString;
32
+ readonly connectionId: Schema.NonEmptyString;
33
+ readonly integrationId: Schema.NonEmptyString;
34
+ readonly expiresAt: Schema.Number;
35
+ readonly requestId: Schema.NonEmptyString;
36
+ readonly actorUserId: Schema.NonEmptyString;
37
+ readonly holderAgentId: Schema.NonEmptyString;
38
+ readonly repository: Schema.Struct<{
39
+ readonly id: Schema.Number;
40
+ readonly owner: Schema.NonEmptyString;
41
+ readonly name: Schema.NonEmptyString;
42
+ }>;
43
+ }>;
44
+ export type ConnectionAttempt = Schema.Schema.Type<typeof AttemptSchema>;
45
+ /** Custody correlation is independent of the resource receiving the Connection. */
46
+ export type CustodyCorrelation = Pick<ConnectionAttempt, "teamId" | "attemptId" | "actorUserId" | "integrationId">;
47
+ /** Produced only by a custodian's verified completion boundary. */
48
+ export declare const CompletionSchema: Schema.Struct<{
49
+ readonly teamId: Schema.NonEmptyString;
50
+ readonly attemptId: Schema.NonEmptyString;
51
+ readonly principalId: Schema.NonEmptyString;
52
+ readonly integrationId: Schema.NonEmptyString;
53
+ readonly locator: Schema.NonEmptyString;
54
+ }>;
55
+ export type VerifiedCompletion = Schema.Schema.Type<typeof CompletionSchema>;
56
+ export declare const ConnectionReceiptSchema: Schema.Struct<{
57
+ readonly requestId: Schema.NonEmptyString;
58
+ readonly connectionId: Schema.NonEmptyString;
59
+ readonly holderAgentId: Schema.NonEmptyString;
60
+ readonly repository: Schema.Struct<{
61
+ readonly id: Schema.Number;
62
+ readonly owner: Schema.NonEmptyString;
63
+ readonly name: Schema.NonEmptyString;
64
+ }>;
65
+ readonly authorityVersion: Schema.Number;
66
+ readonly status: Schema.Literals<readonly ["active", "disconnected"]>;
67
+ readonly cleanup: Schema.Literals<readonly ["none", "pending"]>;
68
+ }>;
69
+ export type ConnectionReceipt = Schema.Schema.Type<typeof ConnectionReceiptSchema>;
70
+ export declare const ReadBindingSchema: Schema.Struct<{
71
+ readonly teamId: Schema.NonEmptyString;
72
+ readonly holderAgentId: Schema.NonEmptyString;
73
+ readonly connectionId: Schema.NonEmptyString;
74
+ readonly repositoryId: Schema.Number;
75
+ }>;
76
+ export type ReadBinding = Schema.Schema.Type<typeof ReadBindingSchema>;
77
+ export declare const DisconnectInputSchema: Schema.Struct<{
78
+ readonly requestId: Schema.NonEmptyString;
79
+ readonly actorUserId: Schema.NonEmptyString;
80
+ readonly connectionId: Schema.NonEmptyString;
81
+ }>;
82
+ export type DisconnectInput = Schema.Schema.Type<typeof DisconnectInputSchema>;
83
+ /** Private to the Team owner and its trusted custodian adapter. */
84
+ export type RepositoryAccess = Readonly<{
85
+ locator: string;
86
+ repository: RepositoryScope;
87
+ }>;
88
+ export type PreparedCompletion = Readonly<{
89
+ status: "pending";
90
+ attempt: ConnectionAttempt;
91
+ }> | Readonly<{
92
+ status: "completed";
93
+ receipt: ConnectionReceipt;
94
+ }>;
95
+ /** Why a verified completion's Nango connection is not held by the Team. */
96
+ export type OrphanCode = "expired" | "denied" | "conflict" | "reconnect_required";
97
+ /** A verified completion is held (completed), still activatable (pending), or an
98
+ * orphan whose external connection the Team will never hold. */
99
+ export type CompletionDecision = PreparedCompletion | Readonly<{
100
+ status: "orphaned";
101
+ code: OrphanCode;
102
+ }>;
103
+ /** Terminal custodian evidence that the held credential no longer works. */
104
+ export type ReconnectReason = "refresh_failed" | "provider_unauthorized";
105
+ export type ReconnectOutcome = Readonly<{
106
+ status: "reconnect_required";
107
+ connectionId: string;
108
+ }> | Readonly<{
109
+ status: "not_held";
110
+ }>;
111
+ /** Signed Nango auth webhooks this Team handles; every other operation is refused. */
112
+ export type VerifiedWebhook = Readonly<{
113
+ operation: "creation" | "refresh_failed";
114
+ completion: VerifiedCompletion;
115
+ }>;
116
+ export type CleanupWork = Readonly<{
117
+ connectionId: string;
118
+ authorityVersion: number;
119
+ locator: string;
120
+ }>;
121
+ declare const completionLookupSchema: Schema.Union<readonly [Schema.Struct<{
122
+ readonly status: Schema.Literal<"unresolved">;
123
+ }>, Schema.Struct<{
124
+ readonly status: Schema.Literal<"ambiguous">;
125
+ }>, Schema.Struct<{
126
+ readonly status: Schema.Literal<"verified">;
127
+ readonly completion: Schema.Struct<{
128
+ readonly teamId: Schema.NonEmptyString;
129
+ readonly attemptId: Schema.NonEmptyString;
130
+ readonly principalId: Schema.NonEmptyString;
131
+ readonly integrationId: Schema.NonEmptyString;
132
+ readonly locator: Schema.NonEmptyString;
133
+ }>;
134
+ }>]>;
135
+ /** Private Team operations. Mutations may await the native SQL+wake commit. */
136
+ export type TeamConnectionsOwner = Readonly<{
137
+ begin: (input: ConnectInput) => PreparedCompletion;
138
+ readAttempt: (input: ConnectInput) => PreparedCompletion;
139
+ prepareCompletion: (input: VerifiedCompletion) => CompletionDecision;
140
+ /** Records the settled external deletion of a connection the Team never held. */
141
+ recordOrphan: (input: VerifiedCompletion, code: OrphanCode, custody: "deleted" | "absent") => void;
142
+ /** Marks the active Connection holding this locator as needing reconnection. */
143
+ requireReconnect: (locator: string, reason: ReconnectReason) => ReconnectOutcome;
144
+ activate: (input: VerifiedCompletion, repository: GitHubRepository) => ConnectionReceipt | Promise<ConnectionReceipt>;
145
+ authorize: (binding: ReadBinding) => RepositoryAccess;
146
+ disconnect: (input: DisconnectInput) => ConnectionReceipt | Promise<ConnectionReceipt>;
147
+ nextCleanup: () => CleanupWork | undefined;
148
+ acknowledgeCleanup: (work: CleanupWork) => void;
149
+ }>;
150
+ /** Ordinary Promise port. Only this trusted edge touches external custody. */
151
+ export type CredentialCustodian = Readonly<{
152
+ openSession: (input: Readonly<{
153
+ teamId: string;
154
+ attemptId: string;
155
+ principalId: string;
156
+ integrationId: string;
157
+ }>, signal: AbortSignal) => Promise<Readonly<{
158
+ token: string;
159
+ connectUrl: string;
160
+ expiresAt: number;
161
+ }>>;
162
+ /** Connection creation only; refuses every other signed operation. */
163
+ verifyCompletion: (rawBody: string, signature: string) => Promise<VerifiedCompletion>;
164
+ verifyWebhook: (rawBody: string, signature: string) => Promise<VerifiedWebhook>;
165
+ /** Verified means supported complete correlation, never an arbitrary list-page candidate. */
166
+ findCompletion: (attempt: CustodyCorrelation, signal: AbortSignal) => Promise<Schema.Schema.Type<typeof completionLookupSchema>>;
167
+ /** Only confirmed deletion or a specifically decoded missing-connection result settles cleanup. */
168
+ deleteConnection: (locator: string, signal: AbortSignal) => Promise<"deleted" | "absent">;
169
+ readRepository: (access: RepositoryAccess, signal: AbortSignal) => Promise<string>;
170
+ }>;
171
+ /** Sanitized public failure; an optional cause is retained internally only. */
172
+ export declare class VeilError extends Error {
173
+ readonly code: "denied" | "invalid_input" | "invalid_completion" | "invalid_response" | "conflict" | "ambiguous" | "expired" | "unavailable" | "github_not_configured" | "cancelled" | "reconnect_required" | "unsupported"
174
+ /** GitHub refused the repository: it is outside the App installation. */
175
+ | "repository_not_installed"
176
+ /** The provider's rate limit refused this request; retry later. */
177
+ | "rate_limited";
178
+ constructor(code: "denied" | "invalid_input" | "invalid_completion" | "invalid_response" | "conflict" | "ambiguous" | "expired" | "unavailable" | "github_not_configured" | "cancelled" | "reconnect_required" | "unsupported"
179
+ /** GitHub refused the repository: it is outside the App installation. */
180
+ | "repository_not_installed"
181
+ /** The provider's rate limit refused this request; retry later. */
182
+ | "rate_limited", cause?: unknown);
183
+ }
184
+ /** One Effect policy behind the ordinary connect and GitHub Gatekeeper calls. */
185
+ export declare function createGitHubConnections(options: Readonly<{
186
+ owner: TeamConnectionsOwner;
187
+ custodian: CredentialCustodian;
188
+ }>): {
189
+ connect: (input: ConnectInput, signal?: AbortSignal) => Promise<Readonly<{
190
+ status: "completed";
191
+ receipt: ConnectionReceipt;
192
+ }> | {
193
+ token: string;
194
+ connectUrl: string;
195
+ expiresAt: number;
196
+ status: "pending";
197
+ attemptId: string;
198
+ }>;
199
+ /** The one entry for signed custodian webhooks: activation or reconnect evidence. */
200
+ complete: (rawBody: string, signature: string, signal?: AbortSignal) => Promise<{
201
+ readonly requestId: string;
202
+ readonly connectionId: string;
203
+ readonly holderAgentId: string;
204
+ readonly repository: {
205
+ readonly id: number;
206
+ readonly owner: string;
207
+ readonly name: string;
208
+ };
209
+ readonly authorityVersion: number;
210
+ readonly status: "active" | "disconnected";
211
+ readonly cleanup: "none" | "pending";
212
+ } | ReconnectOutcome>;
213
+ reconcile: (input: ConnectInput, signal?: AbortSignal) => Promise<Readonly<{
214
+ status: "completed";
215
+ receipt: ConnectionReceipt;
216
+ }> | {
217
+ status: "pending";
218
+ attemptId: string;
219
+ }>;
220
+ /** One bounded host recovery call; durable pending work survives interrupted or uncertain deletion. */
221
+ cleanupOne: (signal?: AbortSignal) => Promise<{
222
+ status: "idle";
223
+ connectionId?: never;
224
+ authorityVersion?: never;
225
+ custody?: never;
226
+ } | {
227
+ status: "completed";
228
+ connectionId: string;
229
+ authorityVersion: number;
230
+ custody: "deleted" | "absent";
231
+ }>;
232
+ readRepository: (binding: ReadBinding, signal?: AbortSignal) => Promise<{
233
+ id: number;
234
+ name: string;
235
+ fullName: string;
236
+ private: boolean;
237
+ defaultBranch: string;
238
+ }>;
239
+ disconnect: (input: DisconnectInput) => Promise<{
240
+ readonly requestId: string;
241
+ readonly connectionId: string;
242
+ readonly holderAgentId: string;
243
+ readonly repository: {
244
+ readonly id: number;
245
+ readonly owner: string;
246
+ readonly name: string;
247
+ };
248
+ readonly authorityVersion: number;
249
+ readonly status: "active" | "disconnected";
250
+ readonly cleanup: "none" | "pending";
251
+ }>;
252
+ };
253
+ export type GitHubConnections = ReturnType<typeof createGitHubConnections>;
254
+ export {};