@cotal-ai/auth 0.87.0 → 0.89.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/dist/authority-client.d.ts +1 -21
- package/dist/authority-client.d.ts.map +1 -1
- package/dist/authority-client.js +9 -4
- package/dist/authority-client.js.map +1 -1
- package/dist/idp.d.ts.map +1 -1
- package/dist/idp.js +6 -1
- package/dist/idp.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/issuer.d.ts +4 -0
- package/dist/issuer.d.ts.map +1 -1
- package/dist/issuer.js +6 -1
- package/dist/issuer.js.map +1 -1
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +45 -0
- package/dist/service.js.map +1 -1
- package/dist/tool-result-consent.d.ts +44 -0
- package/dist/tool-result-consent.d.ts.map +1 -0
- package/dist/tool-result-consent.js +135 -0
- package/dist/tool-result-consent.js.map +1 -0
- package/package.json +4 -4
package/dist/service.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { registryStores } from "./lifecycle-registry.js";
|
|
2
|
+
import { createToolResultConsentSigner, makeToolResultConsentRecorder } from "./tool-result-consent.js";
|
|
1
3
|
/**
|
|
2
4
|
* The AUTH SERVICE daemon — the one server-side process a user-auth space runs alongside its broker
|
|
3
5
|
* (the delivery-daemon pattern: spawned detached by `cotal up`, pid-filed per space, torn down by
|
|
@@ -114,6 +116,8 @@ export const MANAGED_RETIRE_PATH = "/managed-lifecycle/retire";
|
|
|
114
116
|
export const VERIFY_ENROLLMENT_PATH = "/manager-service-authority/verify-enrollment";
|
|
115
117
|
/** Host-only, capability-gated proof decision. It neither signs consent nor serves results. */
|
|
116
118
|
export const TOOL_RESULT_HUMAN_PROOF_PATH = "/tool-result-human-proof/verify";
|
|
119
|
+
/** Host-only signed consent issuance, never private result content. */
|
|
120
|
+
export const TOOL_RESULT_CONSENT_PATH = "/tool-result-consent/issue";
|
|
117
121
|
/** Failed-exchange rate limit: at most this many REFUSED exchanges per rolling minute; further
|
|
118
122
|
* attempts get 429 until the window drains. Successes are unthrottled (the CLI's normal path). */
|
|
119
123
|
const FAILED_EXCHANGE_PER_MIN = 30;
|
|
@@ -424,6 +428,7 @@ async function buildAuthAuthorityPlane(opts, unwind, onConnection) {
|
|
|
424
428
|
// the FRESH space-manager-lease holder check) and dispatches.
|
|
425
429
|
const authAdmin = await openAuthAdminListener({ onConnection, server, space, dataAccount, reg: barrierReg, retirement, barrierFlight: retirementFlights, instanceId: authServeInstanceId, epoch: authServeGrant.epoch, grant: authServeGrant, log });
|
|
426
430
|
unwind.push(() => authAdmin.close());
|
|
431
|
+
const recordToolResultConsent = makeToolResultConsentRecorder(registryStores(registry).authKv, () => refuseIfFenced(), space);
|
|
427
432
|
const fileArm = ledgerAuthorizeConnect(opts.dir);
|
|
428
433
|
// The callout's issuance evidence: create-only natkey row, then the connect authority re-read
|
|
429
434
|
// in the same stream before the minted JWT may leave.
|
|
@@ -626,6 +631,7 @@ async function buildAuthAuthorityPlane(opts, unwind, onConnection) {
|
|
|
626
631
|
refuseIfFenced();
|
|
627
632
|
return recordIssuance(i);
|
|
628
633
|
},
|
|
634
|
+
recordToolResultConsent,
|
|
629
635
|
mintConnectCredential: (args) => {
|
|
630
636
|
refuseIfFenced();
|
|
631
637
|
return ensureRootCredential(registry, { ...args, managerInstance: `auth-service:${space}` });
|
|
@@ -1731,6 +1737,10 @@ export async function startAuthService(inputs) {
|
|
|
1731
1737
|
const port = inputs.port ?? 0;
|
|
1732
1738
|
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
1733
1739
|
throw new Error(`auth-service: port must be a port number, got ${port}`);
|
|
1740
|
+
if (inputs.toolResultConsents !== undefined &&
|
|
1741
|
+
(!inputs.toolResultConsents || typeof inputs.toolResultConsents.keyId !== "string" ||
|
|
1742
|
+
!/^[A-Za-z0-9_-]{1,200}$/.test(inputs.toolResultConsents.keyId) || typeof inputs.toolResultConsents.resolveAnchor !== "function"))
|
|
1743
|
+
throw new Error("auth-service: tool-result consent signer requires a key id and fresh anchor resolver");
|
|
1734
1744
|
if (inputs.platformControl !== undefined && typeof inputs.platformControl?.observeAssignment !== "function")
|
|
1735
1745
|
throw new Error("auth-service: platformControl requires an observeAssignment function");
|
|
1736
1746
|
const host = inputs.platformControl?.host;
|
|
@@ -1756,6 +1766,7 @@ export async function startAuthService(inputs) {
|
|
|
1756
1766
|
...(inputs.platformControl !== undefined ? { platformControl: inputs.platformControl } : {}),
|
|
1757
1767
|
...(inputs.platformSupervisor !== undefined ? { platformSupervisor: inputs.platformSupervisor } : {}),
|
|
1758
1768
|
...(inputs.standingRenewableTtlSeconds !== undefined ? { standingRenewableTtlSeconds: inputs.standingRenewableTtlSeconds } : {}),
|
|
1769
|
+
...(inputs.toolResultConsents !== undefined ? { toolResultConsents: inputs.toolResultConsents } : {}),
|
|
1759
1770
|
});
|
|
1760
1771
|
return { ...started.handle, readiness: () => ({ ...started.status(), context: inputs.context }) };
|
|
1761
1772
|
}
|
|
@@ -1850,7 +1861,12 @@ async function startAuthContext(o) {
|
|
|
1850
1861
|
let publicUrl;
|
|
1851
1862
|
const failures = []; // rolling-window timestamps of REFUSED exchanges
|
|
1852
1863
|
const badCaps = []; // rolling-window timestamps of invalid-capability attempts
|
|
1864
|
+
const issueToolResultConsent = o.toolResultConsents === undefined ? undefined : createToolResultConsentSigner({
|
|
1865
|
+
space, accountPublicKey: keys.dataAccount.pub, dir, store: secrets, idp: bridgeIdp, ownerSecret,
|
|
1866
|
+
signer: o.toolResultConsents, record: plane.recordToolResultConsent,
|
|
1867
|
+
});
|
|
1853
1868
|
const ctx = {
|
|
1869
|
+
...(issueToolResultConsent ? { issueToolResultConsent } : {}),
|
|
1854
1870
|
issuer,
|
|
1855
1871
|
bridge,
|
|
1856
1872
|
bridgeIdp,
|
|
@@ -2289,6 +2305,7 @@ const ROUTES = new Map([
|
|
|
2289
2305
|
[MANAGED_RETIRE_PATH, handleManagedLifecycleRetirement],
|
|
2290
2306
|
[VERIFY_ENROLLMENT_PATH, handleVerifyManagedAgentEnrollment],
|
|
2291
2307
|
[TOOL_RESULT_HUMAN_PROOF_PATH, handleToolResultHumanProof],
|
|
2308
|
+
[TOOL_RESULT_CONSENT_PATH, handleToolResultConsent],
|
|
2292
2309
|
]);
|
|
2293
2310
|
/** Route one HTTP request against the loopback route table. Errors are JSON `{ error }`. */
|
|
2294
2311
|
async function handle(req, res, ctx) {
|
|
@@ -2734,6 +2751,34 @@ export async function handleToolResultHumanProof(req, res, ctx) {
|
|
|
2734
2751
|
return send(res, 403, { error: "fresh signed password proof required" });
|
|
2735
2752
|
}
|
|
2736
2753
|
}
|
|
2754
|
+
/** Consent issuance is confined to the authenticated host face and uses sanitized refusals. */
|
|
2755
|
+
export async function handleToolResultConsent(req, res, ctx) {
|
|
2756
|
+
if (req.method !== "POST")
|
|
2757
|
+
return send(res, 405, { error: "POST only" });
|
|
2758
|
+
if (req.headers.origin !== undefined)
|
|
2759
|
+
return send(res, 403, { error: "browser-origin requests are not served here" });
|
|
2760
|
+
if (!/^application\/json\b/.test(req.headers["content-type"] ?? ""))
|
|
2761
|
+
return send(res, 415, { error: "content-type must be application/json" });
|
|
2762
|
+
if (req.headers.authorization !== `Bearer ${ctx.cap}`)
|
|
2763
|
+
return refuseCapability(res, ctx, "missing/invalid exchange capability - consent issuance is a loopback host action");
|
|
2764
|
+
if (!ctx.issueToolResultConsent)
|
|
2765
|
+
return send(res, 503, { error: "tool-result-consent-unavailable", outcome: "not-executed" });
|
|
2766
|
+
let body;
|
|
2767
|
+
try {
|
|
2768
|
+
body = await readJsonBody(req);
|
|
2769
|
+
}
|
|
2770
|
+
catch {
|
|
2771
|
+
return send(res, 400, { error: "invalid consent request", outcome: "not-executed" });
|
|
2772
|
+
}
|
|
2773
|
+
try {
|
|
2774
|
+
return send(res, 200, { consent: await ctx.issueToolResultConsent(body) }, { "cache-control": "no-store" });
|
|
2775
|
+
}
|
|
2776
|
+
catch (error) {
|
|
2777
|
+
const code = error instanceof EpEnvelopeError ? error.code : "unavailable";
|
|
2778
|
+
const status = code === "bad-request" ? 400 : code === "permission-denied" ? 403 : code === "conflict" ? 409 : 503;
|
|
2779
|
+
return send(res, status, { error: `tool-result-consent-${code}`, outcome: error instanceof EpEnvelopeError ? error.outcome ?? "unknown" : "unknown" });
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2737
2782
|
/** The advertised broker address the public bundle carries — the URL participants will dial, so
|
|
2738
2783
|
* the same scheme family `cotal meshes add` accepts. Exported for the producer/consumer contract
|
|
2739
2784
|
* smoke; returns the refusal sentence, or undefined when the address is usable. */
|