authflow-cli 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.
@@ -0,0 +1,80 @@
1
+ {
2
+ "version": 1,
3
+ "cli_version": "0.6.0",
4
+ "invariants": [
5
+ "Serve MCP at /mcp on the origin. The gateway appends /mcp to the origin base URI; do not configure a base URI ending in /mcp or requests will reach /mcp/mcp.",
6
+ "Verify X-Authflow-Identity on every /mcp request. Fail closed with HTTP 403 and no WWW-Authenticate. Never 401 from the origin: that sends clients into the wrong OAuth flow.",
7
+ "Never return a payment URL as tool output. Credit exhaustion is a plain tool error.",
8
+ "Never cache remaining_credits or infer entitlement. Rail owns the live credit counter.",
9
+ "Publish only the gateway URL to clients, never the origin hostname.",
10
+ "Every MCP tool declares title and readOnlyHint/destructiveHint.",
11
+ "Implementation snippets illustrate individual steps; retrieve /docs/protocol/v1 with authflow_get_doc and implement its complete verification algorithm, key-document checks, and conformance checklist before accepting traffic."
12
+ ],
13
+ "verification": [
14
+ "An unsigned POST to the origin /mcp returns 403 without WWW-Authenticate.",
15
+ "Run npx --yes authflow-cli@{{cli_version}} resource verify {{slug}} --issuer {{issuer}}{{workspace_arg}}. Resolve every reported check, then explicitly run npx --yes authflow-cli@{{cli_version}} resource publish {{slug}} --issuer {{issuer}}{{workspace_arg}}.",
16
+ "Add {{resource}} to an MCP client. Authentication begins at the gateway, never at the origin."
17
+ ],
18
+ "metering": {
19
+ "free": "This resource is free. No billing calls, origin API key, or Stripe account are needed. Verify signed identity and serve MCP.",
20
+ "gateway_metered": "This paid resource is gateway_metered: Rail debits the configured tool cost before forwarding. It compensates transport failures before a successful response and isError results only in application/json responses with Content-Length at most 256 KiB. SSE, chunked responses, and failures after successful headers cannot be inspected for refunds; use origin_reported when exact streaming or work-dependent compensation is required. Do not write any billing code in the origin for gateway metering. No origin API key is needed for this path. Configure metering_default_units and tool_units through resource update; policy changes require verification and explicit republication.",
21
+ "origin_reported": "This paid resource is origin_reported: debit before calling your provider and issue a compensating credit if work fails. Store the origin API key directly in a secret store. To move metering to Rail, update metering_mode to gateway_metered with the intended costs, remove origin debit calls, verify, and explicitly republish."
22
+ },
23
+ "secrets": {
24
+ "key": "Authflow:OriginApiKey",
25
+ "env_var": "AUTHFLOW_ORIGIN_API_KEY",
26
+ "note": "Only paid origin_reported resources need this credential. Deliver it directly with authflow resource rotate-key and an explicit secret-store destination. Store it in user-secrets or your deployment secret store, never in a committed config file or tool/chat output."
27
+ },
28
+ "origin_reported_steps": [
29
+ {
30
+ "title": "Deliver the usage credential securely",
31
+ "action": "First sign in using npx --yes authflow-cli@{{cli_version}} login --issuer {{issuer}}{{workspace_arg}}. Then run npx --yes authflow-cli@{{cli_version}} resource rotate-key {{slug}} --issuer {{issuer}}{{workspace_arg}} with --write-secrets user-secrets --secret-destination <project.csproj>, or --write-secrets env-file --secret-destination <private-path>. Confirm the exact local destination before running it. The CLI writes directly there and does not deploy or change remote app settings. Never ask the user to paste the credential into chat."
32
+ },
33
+ {
34
+ "title": "Debit before calling your provider",
35
+ "action": "POST {{issuer}}/origin/v1/usage with the stored Bearer origin credential and application/json. Set Idempotency-Key equal to the body's idempotency_key. Include event_type=debit, subject/resource from the verified request, units, occurred_at in UTC, stable request_id, tool, and identity_nonce from that request. Debit BEFORE the provider call; never start work after a rejected or unresolved debit. On insufficient_credits, return a tool error without a payment URL. For transient failures, retry the same logical event with the same idempotency key as specified in Origin Protocol section 7."
36
+ },
37
+ {
38
+ "title": "Compensate failed work",
39
+ "action": "If provider work or artifact persistence fails after a successful debit, post event_type=credit with a separate stable idempotency key, the same subject/resource, positive units, UTC occurred_at, request_id, compensates equal to the original debit idempotency key, and reason provider_failure, origin_cancelled, or unsuccessful_result. Do not send identity_nonce on credit. Follow Origin Protocol sections 7 and 8, including compensation retry and durability."
40
+ }
41
+ ],
42
+ "frameworks": {
43
+ "dotnet": {
44
+ "summary": "ASP.NET Core origin using Authflow.Origin for signed identity verification and, when needed, usage reporting.",
45
+ "steps": [
46
+ { "title": "Add the package", "action": "Add the prerelease Authflow.Origin package to a .NET 9 or .NET 10 MCP server project. Use the sidecar for older runtimes.", "command": "dotnet add package Authflow.Origin --prerelease" },
47
+ { "title": "Wire the middleware", "action": "Import Authflow.Origin, register services, warm verification keys at startup, and run identity middleware before mapping MCP. Keep the existing MCP server/tool registrations.", "file": "Program.cs", "snippet": "using Authflow.Origin;\nbuilder.Services.AddAuthflowOrigin(builder.Configuration);\nvar app = builder.Build();\nawait app.WarmAuthflowOriginKeysAsync();\napp.UseAuthflowOrigin();\napp.MapMcp(\"/mcp\");" },
48
+ { "title": "Configure issuer and resource", "action": "Set Authflow:Issuer to {{issuer}} and Authflow:Resource to {{resource}}. These values are public configuration; environment equivalents are Authflow__Issuer and Authflow__Resource." },
49
+ { "title": "Read the verified caller", "action": "Use the verified identity rather than caller-supplied user or tier values.", "snippet": "var identity = context.RequireAuthflowIdentity(); // identity.Subject, identity.Tier" },
50
+ { "title": "Share replay protection before scaling", "action": "The default MemoryNonceStore is per-process. Before deploying multiple origin replicas, register an INonceStore backed by a shared atomic store before AddAuthflowOrigin. Return NonceClaim.Unavailable when the store cannot answer so middleware fails closed." }
51
+ ]
52
+ },
53
+ "typescript": {
54
+ "summary": "TypeScript/Node origin implementing the published Origin Protocol v1. There is no published npm middleware yet.",
55
+ "steps": [
56
+ { "title": "Fetch and cache verification keys", "action": "At startup GET {{issuer}}/.well-known/authflow-origin-keys. Cap the cache at 3600 seconds, refresh once for an unknown kid, retain the last good document on refresh failure, and require an exact configured issuer match." },
57
+ { "title": "Verify identity on every /mcp request", "action": "Implement every check in Origin Protocol section 4: exactly one header, v1 compact form, canonical unpadded base64url, valid UTF-8/JSON and required claim types, forbidden balance members, exact issuer/resource, integer iat/exp with exp > iat and TTL <= 300 seconds, 60 second clock leeway, and atomic nonce replay prevention until exp plus leeway. Fail closed on unavailable replay storage. Verify Ed25519 using the matching validated public JWK; this snippet is only the signature step.", "snippet": "import { verify, createPublicKey } from \"node:crypto\";\nconst publicKey = createPublicKey({ key: jwk, format: \"jwk\" });\nconst signature = Buffer.from(signatureB64, \"base64url\"); // after canonical encoding checks\nconst signingInput = Buffer.from(`v1;${claimsB64}`, \"ascii\");\nif (!verify(null, signingInput, publicKey, signature)) throw new Error(\"bad_signature\");" },
58
+ { "title": "Return fail-closed 403", "action": "Reject failed verification with HTTP 403, no WWW-Authenticate, and the exact Origin Protocol section 6.2 JSON shape: error invalid_origin_identity and the applicable error_code." }
59
+ ]
60
+ },
61
+ "python": {
62
+ "summary": "Python origin implementing the published Origin Protocol v1. There is no published Python middleware yet.",
63
+ "steps": [
64
+ { "title": "Install an Ed25519 implementation", "action": "Python has no standard-library Ed25519 verifier.", "command": "pip install cryptography" },
65
+ { "title": "Fetch and cache verification keys", "action": "At startup GET {{issuer}}/.well-known/authflow-origin-keys. Cap the cache at 3600 seconds, refresh once on unknown kid, retain the last good document on failure, and require an exact issuer match." },
66
+ { "title": "Verify identity on every /mcp request", "action": "Implement every check in Origin Protocol section 4, including one header, canonical unpadded base64url, valid UTF-8/JSON, required claim types, forbidden balance members, exact issuer/resource, integer iat/exp with exp > iat and TTL <= 300 seconds, 60 second clock leeway, and atomic nonce replay prevention until exp plus leeway. Fail closed on unavailable replay storage. In this signature-only snippet, public_key_bytes is the decoded 32-byte x from the matching validated JWK and signature is the canonical-decoded 64-byte signature.", "snippet": "from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey\nsigning_input = b\"v1;\" + claims_b64.encode(\"ascii\")\nEd25519PublicKey.from_public_bytes(public_key_bytes).verify(signature, signing_input)" },
67
+ { "title": "Return fail-closed 403", "action": "Reject failed verification with HTTP 403 and the Origin Protocol section 6.2 body. Never attach WWW-Authenticate." }
68
+ ]
69
+ },
70
+ "proxy": {
71
+ "summary": "Run the Authflow origin proxy in front of an existing MCP server. The proxy verifies identity; keep the upstream private.",
72
+ "steps": [
73
+ { "title": "Register the proxy as the origin", "action": "Set origin_base_uri in origin.json to the proxy's URL, not the upstream MCP server's URL.", "command": "npx --yes authflow-cli@{{cli_version}} resource origin {{slug}} --issuer {{issuer}}{{workspace_arg}} --file origin.json" },
74
+ { "title": "Generate sidecar configuration", "action": "Save the public resource receipt, then scaffold the sidecar configuration.", "command": "authflow scaffold --receipt receipt.json --upstream http://upstream:3000" },
75
+ { "title": "Consume forwarded identity", "action": "The upstream receives X-Authflow-User, X-Authflow-Tier, and X-Authflow-Nonce after verification. Trust them only when the proxy is the sole ingress. The proxy does not implement your origin_reported billing logic." },
76
+ { "title": "Set a shared nonce cache before scaling", "action": "Replay protection defaults to process-local. Set Authflow__NonceCacheRedis before using more than one replica." }
77
+ ]
78
+ }
79
+ }
80
+ }
@@ -1,44 +1,38 @@
1
1
  import type { RegistrationReceipt } from "./config.js";
2
- /**
3
- * Framework-specific integration instructions, emitted as structured data rather than prose.
4
- *
5
- * The consumer is usually not a human: it is the creator's own coding agent, which already has the
6
- * codebase open. The rail cannot see their files and should not try to — it supplies the plan, the
7
- * agent applies it. That split is also what keeps this honest as a public contract: everything here
8
- * is derived from the receipt and the published spec, never from rail internals.
9
- */
10
2
  export declare const integrationFrameworks: readonly ["dotnet", "typescript", "python", "proxy"];
11
3
  export type IntegrationFramework = (typeof integrationFrameworks)[number];
12
4
  export type IntegrationStep = {
13
5
  title: string;
14
- /** What the agent should do. Imperative, one action. */
15
6
  action: string;
16
- /** Shell command to run, when the step is a command. */
17
7
  command?: string;
18
- /** File content or snippet to apply, when the step is an edit. */
19
8
  snippet?: string;
20
- /** Where the snippet goes. */
21
9
  file?: string;
22
10
  };
11
+ type SecretDelivery = {
12
+ key: string;
13
+ env_var: string;
14
+ note: string;
15
+ };
23
16
  export type IntegrationPlan = {
24
17
  framework: IntegrationFramework;
25
18
  summary: string;
26
19
  configuration: Record<string, string>;
27
- secrets: {
28
- key: string;
29
- envVar: string;
30
- note: string;
31
- };
20
+ secrets: SecretDelivery | null;
32
21
  steps: IntegrationStep[];
33
22
  verification: string[];
34
23
  invariants: string[];
35
24
  metering: string;
25
+ access_mode: string;
26
+ metering_mode: string;
27
+ metering_default_units: number;
28
+ tool_units: Record<string, number>;
29
+ template_version: number;
30
+ workspace_id: string | null;
31
+ status: string | null;
32
+ pending_requirements: string[];
33
+ allowed_actions: string[];
36
34
  };
37
35
  export declare function isIntegrationFramework(value: string): value is IntegrationFramework;
38
- export declare function buildIntegrationPlan(framework: IntegrationFramework, receipt: RegistrationReceipt): IntegrationPlan;
39
- /**
40
- * Origins that bill exactly one unit per tool call can skip debit/compensate entirely by declaring
41
- * per-tool costs at registration. It is the largest and subtlest part of the integration, so the
42
- * plan should say so rather than leaving it to be discovered in §9 of the quickstart.
43
- */
36
+ export declare function buildIntegrationPlan(framework: IntegrationFramework, receipt: RegistrationReceipt, workspaceId?: string): IntegrationPlan;
44
37
  export declare function meteringAdvice(gatewayMetered: boolean): string;
38
+ export {};
@@ -1,247 +1,40 @@
1
- import { originApiKeyConfigKey, originApiKeyEnvVar } from "./secrets.js";
2
- /**
3
- * Framework-specific integration instructions, emitted as structured data rather than prose.
4
- *
5
- * The consumer is usually not a human: it is the creator's own coding agent, which already has the
6
- * codebase open. The rail cannot see their files and should not try to — it supplies the plan, the
7
- * agent applies it. That split is also what keeps this honest as a public contract: everything here
8
- * is derived from the receipt and the published spec, never from rail internals.
9
- */
1
+ import { existsSync, readFileSync } from "node:fs";
10
2
  export const integrationFrameworks = ["dotnet", "typescript", "python", "proxy"];
3
+ // npm packages carry the same canonical file embedded by Rail.Registry. Source execution reads
4
+ // the repository asset. Templates contain no credentials and there is no separate CLI guide.
5
+ const packaged = new URL("./integration-plans.json", import.meta.url);
6
+ const source = new URL("../../../docs/onboarding/integration-plans.json", import.meta.url);
7
+ const templates = JSON.parse(readFileSync(existsSync(packaged) ? packaged : source, "utf8"));
11
8
  export function isIntegrationFramework(value) {
12
9
  return integrationFrameworks.includes(value);
13
10
  }
14
- export function buildIntegrationPlan(framework, receipt) {
15
- return {
16
- ...frameworkPlan(framework, receipt),
17
- metering: meteringAdvice(receipt.metering_mode === "gateway_metered"),
18
- };
19
- }
20
- function frameworkPlan(framework, receipt) {
11
+ export function buildIntegrationPlan(framework, receipt, workspaceId) {
21
12
  const issuer = receipt.issuer ?? "";
22
13
  const resource = receipt.resource ?? receipt.gateway_url ?? "";
23
- const slug = receipt.slug ?? "";
24
- const configuration = {
25
- AUTHFLOW_ISSUER: issuer,
26
- AUTHFLOW_RESOURCE: resource,
27
- };
28
- const secrets = {
29
- key: originApiKeyConfigKey,
30
- envVar: originApiKeyEnvVar,
31
- note: "Shown once at registration. Store it in a secret store (dotnet user-secrets locally, Key " +
32
- "Vault in Azure) — never in a committed config file or a receipt left on disk.",
14
+ const access = receipt.access_mode ?? "paid";
15
+ const mode = receipt.metering_mode ?? "origin_reported";
16
+ const billing = access === "free" ? "free" : mode;
17
+ if (!templates.metering[billing])
18
+ throw new Error("The resource has an unsupported metering mode.");
19
+ const render = (text) => text.replaceAll("{{issuer}}", issuer).replaceAll("{{resource}}", resource).replaceAll("{{slug}}", receipt.slug ?? "")
20
+ .replaceAll("{{cli_version}}", templates.cli_version).replaceAll("{{workspace_arg}}", workspaceId ? ` --workspace ${workspaceId}` : "");
21
+ const configuration = { AUTHFLOW_ISSUER: issuer, AUTHFLOW_RESOURCE: resource };
22
+ if (framework === "proxy")
23
+ configuration.AUTHFLOW_UPSTREAM_BASE_URI = "http://upstream:3000";
24
+ const template = templates.frameworks[framework];
25
+ return {
26
+ framework, summary: template.summary, configuration,
27
+ secrets: billing === "origin_reported" ? { ...templates.secrets } : null,
28
+ steps: [...template.steps, ...(billing === "origin_reported" ? templates.origin_reported_steps : [])]
29
+ .map(step => Object.fromEntries(Object.entries(step).map(([key, value]) => [key, render(value)]))),
30
+ verification: templates.verification.map(render), invariants: [...templates.invariants],
31
+ metering: templates.metering[billing], access_mode: access, metering_mode: mode,
32
+ metering_default_units: receipt.metering_default_units ?? 1, tool_units: { ...receipt.tool_units },
33
+ template_version: templates.version, workspace_id: workspaceId ?? null, status: receipt.status ?? null,
34
+ pending_requirements: [...receipt.pending_requirements ?? []], allowed_actions: [...receipt.allowed_actions ?? []],
33
35
  };
34
- const invariants = [
35
- "Serve MCP at /mcp on the origin. The gateway appends /mcp to the origin base URI, so an " +
36
- "origin base URI that already ends in /mcp would be called at /mcp/mcp.",
37
- "Verify X-Authflow-Identity on every request to /mcp and fail closed with HTTP 403 and no " +
38
- "WWW-Authenticate. Never 401 from the origin: that kicks clients into OAuth against you.",
39
- "Never return a payment URL as tool output. Credit exhaustion is a plain tool error.",
40
- "Never cache remaining_credits or infer entitlements. The rail's counter is the only source.",
41
- "Publish only the gateway URL to clients. The origin hostname is never client-facing.",
42
- "Every MCP tool must declare title and readOnlyHint/destructiveHint.",
43
- ];
44
- const verification = [
45
- "Unauthenticated POST to the origin's /mcp returns 403 with no WWW-Authenticate.",
46
- `Run the conformance probe (authflow probe) — it must pass before the resource takes paid traffic.`,
47
- `Add the gateway URL ${resource} to Claude or Cursor. First connect must 401 from the gateway, not from the origin.`,
48
- ];
49
- switch (framework) {
50
- case "dotnet":
51
- return {
52
- framework,
53
- summary: "ASP.NET Core origin using the Authflow.Origin package, which implements identity-header " +
54
- "verification and the usage-reporting client from the published spec.",
55
- configuration,
56
- secrets,
57
- steps: [
58
- {
59
- title: "Add the package",
60
- action: "Add the Authflow.Origin NuGet package to the MCP server project.",
61
- command: "dotnet add package Authflow.Origin",
62
- },
63
- {
64
- title: "Wire the middleware",
65
- action: "Register Authflow services, warm the key ring at startup, and add the middleware " +
66
- "before the MCP endpoints are mapped so unsigned requests never reach a tool.",
67
- file: "Program.cs",
68
- snippet: [
69
- "builder.Services.AddAuthflowOrigin(builder.Configuration);",
70
- "",
71
- "var app = builder.Build();",
72
- "",
73
- "await app.WarmAuthflowOriginKeysAsync();",
74
- "app.UseAuthflowOrigin();",
75
- "app.MapMcp(\"/mcp\");",
76
- ].join("\n"),
77
- },
78
- {
79
- title: "Configure issuer and resource",
80
- action: "Add the Authflow section to appsettings.json (these are not secrets).",
81
- file: "appsettings.json",
82
- snippet: JSON.stringify({ Authflow: { Issuer: issuer, Resource: resource } }, null, 2),
83
- },
84
- {
85
- title: "Store the origin API key",
86
- action: "Put the origin API key in user-secrets. The CLI can do this for you at registration " +
87
- "with --write-secrets user-secrets.",
88
- command: `dotnet user-secrets set "${originApiKeyConfigKey}" "<origin-api-key>"`,
89
- },
90
- {
91
- title: "Read the verified caller",
92
- action: "In each billable tool, take the caller from the verified identity rather than from " +
93
- "anything client-supplied.",
94
- snippet: "var identity = context.RequireAuthflowIdentity(); // identity.Subject, identity.Tier",
95
- },
96
- ],
97
- verification,
98
- invariants,
99
- };
100
- case "typescript":
101
- return {
102
- framework,
103
- summary: "TypeScript/Node origin implementing Origin Protocol v1 in-process. There is no published " +
104
- "npm middleware yet, so the verification steps are implemented against the spec.",
105
- configuration,
106
- secrets,
107
- steps: [
108
- {
109
- title: "Fetch and cache the verification keys",
110
- action: `At startup, GET ${issuer}${"/.well-known/authflow-origin-keys"} and cache it. Cap the ` +
111
- "cache at 3600s, refresh once on an unknown kid, and keep the last good document if a " +
112
- "refresh fails. Reject any document whose issuer is not exactly your configured issuer.",
113
- },
114
- {
115
- title: "Verify the identity header on every /mcp request",
116
- action: "Parse X-Authflow-Identity as v1;<claims_b64>;<signature_b64>. Verify the Ed25519 " +
117
- "signature over the ASCII bytes of \"v1;\" + claims_b64. Then check the claims: issuer " +
118
- "and resource byte-equal to your configuration, iat/exp within 60s leeway, TTL <= 300s, " +
119
- "and the nonce unseen. Reject with 403 and the exact spec §6.2 body.",
120
- snippet: [
121
- "// Node has Ed25519 built in — no dependency needed.",
122
- "import { verify, createPublicKey } from \"node:crypto\";",
123
- "",
124
- "const signingInput = Buffer.from(`v1;${claimsB64}`, \"ascii\");",
125
- "const ok = verify(null, signingInput, publicKey, signature);",
126
- ].join("\n"),
127
- },
128
- {
129
- title: "Return the fail-closed 403",
130
- action: "On any verification failure, return this exact shape. No WWW-Authenticate header.",
131
- snippet: JSON.stringify({ error: "invalid_origin_identity", error_code: "<spec §6.2 code>" }, null, 2),
132
- },
133
- {
134
- title: "Debit before calling your provider",
135
- action: `POST ${issuer}${"/origin/v1/usage"} with Authorization: Bearer <origin API key> and an ` +
136
- "Idempotency-Key header equal to the body's idempotency_key. Do this BEFORE the provider " +
137
- "call. On 409 insufficient_credits, return a tool error with no payment URL.",
138
- },
139
- {
140
- title: "Compensate on failure",
141
- action: "If the provider call or artifact persistence fails after a successful debit, POST a " +
142
- "credit whose compensates field is the debit's idempotency key.",
143
- },
144
- ],
145
- verification,
146
- invariants,
147
- };
148
- case "python":
149
- return {
150
- framework,
151
- summary: "Python origin implementing Origin Protocol v1 in-process. No published package yet, so " +
152
- "verification is implemented against the spec.",
153
- configuration,
154
- secrets,
155
- steps: [
156
- {
157
- title: "Install an Ed25519 implementation",
158
- action: "Python has no stdlib Ed25519 verifier.",
159
- command: "pip install cryptography",
160
- },
161
- {
162
- title: "Fetch and cache the verification keys",
163
- action: `At startup, GET ${issuer}${"/.well-known/authflow-origin-keys"} and cache it (cap 3600s, ` +
164
- "one forced refresh on unknown kid, keep last good on failure). Reject a document whose " +
165
- "issuer is not exactly your configured issuer.",
166
- },
167
- {
168
- title: "Verify the identity header on every /mcp request",
169
- action: "Parse v1;<claims_b64>;<signature_b64>, verify Ed25519 over b\"v1;\" + claims_b64, then " +
170
- "validate issuer, resource, iat/exp (60s leeway), TTL <= 300s, and nonce replay.",
171
- snippet: [
172
- "from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey",
173
- "",
174
- "signing_input = b\"v1;\" + claims_b64.encode(\"ascii\")",
175
- "Ed25519PublicKey.from_public_bytes(x).verify(signature, signing_input)",
176
- ].join("\n"),
177
- },
178
- {
179
- title: "Return the fail-closed 403",
180
- action: "On any verification failure return the spec §6.2 body with HTTP 403 and no WWW-Authenticate.",
181
- },
182
- {
183
- title: "Debit before the provider, compensate on failure",
184
- action: `POST ${issuer}${"/origin/v1/usage"} with a Bearer origin API key and an Idempotency-Key ` +
185
- "header matching the body. Debit before the provider call; credit with compensates set " +
186
- "if the user does not get a successful result.",
187
- },
188
- ],
189
- verification,
190
- invariants,
191
- };
192
- case "proxy":
193
- return {
194
- framework,
195
- summary: "Language-agnostic: run the authflow-origin-proxy sidecar in front of the existing MCP " +
196
- "server. The proxy verifies the identity header and forwards plain headers upstream, so " +
197
- "the MCP server itself needs no changes.",
198
- configuration: {
199
- ...configuration,
200
- AUTHFLOW_UPSTREAM_BASE_URI: "http://upstream:3000",
201
- },
202
- secrets,
203
- steps: [
204
- {
205
- title: "Register the proxy as the origin",
206
- action: "The proxy is what the gateway talks to, so origin_base_uri must be the proxy's URL, " +
207
- "not the upstream MCP server's.",
208
- command: `authflow set-origin --slug ${slug} --origin-url https://your-proxy.example`,
209
- },
210
- {
211
- title: "Generate the compose and env files",
212
- action: "Scaffold the sidecar configuration from the receipt.",
213
- command: "authflow scaffold --receipt receipt.json --upstream http://upstream:3000",
214
- },
215
- {
216
- title: "Consume the forwarded identity",
217
- action: "Upstream receives X-Authflow-User, X-Authflow-Tier, and X-Authflow-Nonce after " +
218
- "verification. Trust these only because the proxy is the sole ingress — do not expose " +
219
- "the upstream directly.",
220
- },
221
- {
222
- title: "Set a shared nonce cache before scaling",
223
- action: "Replay protection is process-local by default, so more than one replica reopens a " +
224
- "replay window. Set Authflow__NonceCacheRedis before raising replicas above 1.",
225
- },
226
- ],
227
- verification,
228
- invariants,
229
- };
230
- }
231
36
  }
232
- /**
233
- * Origins that bill exactly one unit per tool call can skip debit/compensate entirely by declaring
234
- * per-tool costs at registration. It is the largest and subtlest part of the integration, so the
235
- * plan should say so rather than leaving it to be discovered in §9 of the quickstart.
236
- */
237
37
  export function meteringAdvice(gatewayMetered) {
238
- return gatewayMetered
239
- ? "This resource is gateway_metered: the rail debits before forwarding, returns the credit-exhaustion " +
240
- "tool error itself, and compensates automatically on a transport error or an isError result. " +
241
- "Do not write any billing code in the origin. Verify the identity header and serve MCP."
242
- : "This resource is origin_reported: the origin must debit before calling its provider and post a " +
243
- "compensating credit when the user does not get a successful result. If you bill one unit per " +
244
- "tool call and do not stream responses, re-registering as gateway_metered removes all of that " +
245
- "code — it is a registration change, not an origin change.";
38
+ return templates.metering[gatewayMetered ? "gateway_metered" : "origin_reported"];
246
39
  }
247
40
  //# sourceMappingURL=integration.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type LoginCredential } from "./credentials.js";
2
+ export declare function validatedIssuer(value: string): string;
3
+ export declare function matchesOAuthState(actual: string | null, expected: string): boolean;
4
+ export declare function login(issuerValue: string, workspaceId?: string, open?: boolean): Promise<{
5
+ issuer: string;
6
+ workspace_id: string;
7
+ }>;
8
+ export declare function managementCredential(issuerValue: string): Promise<LoginCredential>;
9
+ export declare function logout(issuerValue: string, options?: {
10
+ localOnly?: boolean;
11
+ }): Promise<void>;