@cotal-ai/auth 0.58.0 → 0.60.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.map +1 -1
- package/dist/authority-client.js +5 -1
- package/dist/authority-client.js.map +1 -1
- package/dist/barrier-evict.d.ts +7 -0
- package/dist/barrier-evict.d.ts.map +1 -1
- package/dist/barrier-evict.js +83 -21
- package/dist/barrier-evict.js.map +1 -1
- package/dist/callout.d.ts.map +1 -1
- package/dist/callout.js +9 -3
- package/dist/callout.js.map +1 -1
- package/dist/commands.js +18 -11
- package/dist/commands.js.map +1 -1
- package/dist/derive.d.ts +5 -0
- package/dist/derive.d.ts.map +1 -1
- package/dist/derive.js +15 -1
- package/dist/derive.js.map +1 -1
- package/dist/grant-command.d.ts +9 -8
- package/dist/grant-command.d.ts.map +1 -1
- package/dist/grant-command.js +9 -8
- package/dist/grant-command.js.map +1 -1
- package/dist/idp.d.ts +10 -1
- package/dist/idp.d.ts.map +1 -1
- package/dist/idp.js +11 -3
- package/dist/idp.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/issuer.d.ts +3 -1
- package/dist/issuer.d.ts.map +1 -1
- package/dist/issuer.js +6 -2
- package/dist/issuer.js.map +1 -1
- package/dist/ledger.d.ts +6 -7
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +1 -1
- package/dist/ledger.js.map +1 -1
- package/dist/managed-agent-enrollment.d.ts +29 -1
- package/dist/managed-agent-enrollment.d.ts.map +1 -1
- package/dist/managed-agent-enrollment.js +45 -1
- package/dist/managed-agent-enrollment.js.map +1 -1
- package/dist/manager-admin-authorization.d.ts +13 -4
- package/dist/manager-admin-authorization.d.ts.map +1 -1
- package/dist/manager-admin-authorization.js +15 -7
- package/dist/manager-admin-authorization.js.map +1 -1
- package/dist/manager-authority.d.ts +11 -7
- package/dist/manager-authority.d.ts.map +1 -1
- package/dist/manager-authority.js +9 -7
- package/dist/manager-authority.js.map +1 -1
- package/dist/manager-goal-index.d.ts +2 -3
- package/dist/manager-goal-index.d.ts.map +1 -1
- package/dist/manager-goal-index.js +2 -2
- package/dist/manager-goal-index.js.map +1 -1
- package/dist/manager-maintenance.d.ts +2 -3
- package/dist/manager-maintenance.d.ts.map +1 -1
- package/dist/manager-maintenance.js +8 -2
- package/dist/manager-maintenance.js.map +1 -1
- package/dist/permissions.d.ts +15 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +20 -1
- package/dist/permissions.js.map +1 -1
- package/dist/platform-control.d.ts +47 -0
- package/dist/platform-control.d.ts.map +1 -0
- package/dist/platform-control.js +117 -0
- package/dist/platform-control.js.map +1 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +36 -11
- package/dist/provider.js.map +1 -1
- package/dist/records-scanner.d.ts.map +1 -1
- package/dist/records-scanner.js +3 -2
- package/dist/records-scanner.js.map +1 -1
- package/dist/retained-manager-validation.d.ts +9 -6
- package/dist/retained-manager-validation.d.ts.map +1 -1
- package/dist/retained-manager-validation.js +9 -7
- package/dist/retained-manager-validation.js.map +1 -1
- package/dist/service.d.ts +75 -29
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +127 -31
- package/dist/service.js.map +1 -1
- package/dist/session-redemption.d.ts +51 -0
- package/dist/session-redemption.d.ts.map +1 -0
- package/dist/session-redemption.js +61 -0
- package/dist/session-redemption.js.map +1 -0
- package/dist/token.d.ts +15 -2
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +30 -3
- package/dist/token.js.map +1 -1
- package/package.json +3 -3
package/dist/service.js
CHANGED
|
@@ -61,15 +61,17 @@ import { Kvm } from "@nats-io/kv";
|
|
|
61
61
|
import { contractRefToHex, contractStoreContext, fetchContractArtifact, verifyClusterManifest, verifyClusterRoot, admissionBucket, admissionMediatorGrants, assertDerivedOwnerToken, assertLifecycleToken, assertValidOwnerToken, authorizeTrustedServeSnapshot, commitSiblingIssuance, credsClaims, EpEnvelopeError, ensureAuthorityStores, epAuthBucket, isReachable, jwtFromCreds, managedRetirementOpId, mintCreds, mintPublicUserJwt, newIdentity, observeHostedRunAttempt, openRecordsBucket, parseEndpointGate, parseServiceSpec, parseServiceStatus, rawDigest, parseSecretStoreIdentity, readCheckpointStatus, readRunAdmission, readRunRecord, readSvcRecordLeader, reconcileEndpointGate, sameSecretStoreIdentity, recordSpecKey, recordStatusKey, RECORD_KINDS, recordsBucket, remoteManagerActors, retirementFrontierStreams, runDriverCaller, serveIssuanceGateKv, standaloneConnectOpts, STANDING_RENEWABLE_TTL_SEC, withIssuerSession, authorizeServeGrant, contractArtifactCanonicalBytes, DEV_OWNER, endpointRegistrationBarrier, mintLifecycleUid, principalKey, provisionEndpointGateOpen, publishContractArtifact, registerServiceInstance, SERVICE_READY, writeServiceStatus } from "@cotal-ai/core";
|
|
62
62
|
import { findCotalRoot, loadManagerInstanceIdentity, userAuthStateDir, workspaceSecretStore, createAuthInstanceIdentity, loadAuthInstanceIdentity } from "@cotal-ai/workspace";
|
|
63
63
|
import { decodeJwt } from "jose";
|
|
64
|
-
import { deriveOwnerForIdpSubject } from "./derive.js";
|
|
64
|
+
import { deriveOwnerForIdpSubject, platformControlOwner } from "./derive.js";
|
|
65
|
+
import { makePlatformControlAuthority, requireManagerAuthorityHolder } from "./platform-control.js";
|
|
65
66
|
import { startAuthCallout } from "./callout.js";
|
|
66
67
|
import { createIdpBridge, verifyIdpToken } from "./idp.js";
|
|
67
68
|
import { PUBLIC_EXCHANGE_VIEWS } from "./token.js";
|
|
69
|
+
import { grantCoordinates, verifySessionRedemption } from "./session-redemption.js";
|
|
68
70
|
import { pinnedJwksResolver } from "./issuer.js";
|
|
69
71
|
import { calloutPermissions } from "./permissions.js";
|
|
70
72
|
import { admitRemoteRun, authorizeRemoteManagerRenewal, authorizeRemoteRunAttempt, issueRemoteManagerAuthority, parseRemoteRunAdmissionRequest, parseRemoteRunAttemptRequest } from "./manager-authority.js";
|
|
71
73
|
import { authorizeRemoteRetainedAgentValidation, completeRemoteRetainedAgentValidation, remoteManagerCurrentRegistrationProof } from "./retained-manager-validation.js";
|
|
72
|
-
import { authorizeRemoteManagedAgentEnrollment, authorizeRemoteManagedAgentPrepareRetirement } from "./managed-agent-enrollment.js";
|
|
74
|
+
import { authorizeRemoteManagedAgentEnrollment, authorizeRemoteManagedAgentPrepareRetirement, authorizeRemoteManagedAgentRuntimeCreate, authorizeRemoteManagedAgentRuntimeStatus } from "./managed-agent-enrollment.js";
|
|
73
75
|
import { authorizeRemoteManagerGoalIndexScan, completeRemoteManagerGoalIndexScan } from "./manager-goal-index.js";
|
|
74
76
|
import { authorizeRemoteManagerAdmin } from "./manager-admin-authorization.js";
|
|
75
77
|
import { authorizeRemoteManagerMaintenance, completeRemoteManagerMaintenance } from "./manager-maintenance.js";
|
|
@@ -83,7 +85,7 @@ import { openAuthLedgerScannerCandidate } from "./ledger-scanner.js";
|
|
|
83
85
|
import { openRecordsScannerCandidate } from "./records-scanner.js";
|
|
84
86
|
import { acquirePlaneClaim, makeDeliveryAdminPlaneOracle, makeDeliveryAdminPrincipalOracle, scannerDeathCopy } from "./plane-claim.js";
|
|
85
87
|
import { enumerateOperationIntents, resumeAgentTakeover } from "./credential-ledger.js";
|
|
86
|
-
import { makeDeliveryAdminEvictor } from "./barrier-evict.js";
|
|
88
|
+
import { makeDeliveryAdminEvictor, makeDeliveryAdminHolderEvictor } from "./barrier-evict.js";
|
|
87
89
|
import { resumeAgentRetirement, runAgentRetirementBarrier } from "./retirement-barrier.js";
|
|
88
90
|
import { makeRetirementCleaners } from "./retirement-cleaner.js";
|
|
89
91
|
import { makeDrainRepairers } from "./drain-repair.js";
|
|
@@ -546,6 +548,18 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
546
548
|
if (verdict !== "candidate")
|
|
547
549
|
throw new Error(`manager instance ${instanceId} is ${verdict} at the permission mint`);
|
|
548
550
|
},
|
|
551
|
+
verifySession: async (principal, claim) => {
|
|
552
|
+
refuseIfFenced();
|
|
553
|
+
return verifySessionRedemption({
|
|
554
|
+
jsm: recordsJsm,
|
|
555
|
+
space,
|
|
556
|
+
managerGate: async (owner, instanceId) => {
|
|
557
|
+
const verdict = await managerGate(owner, assertLifecycleToken(instanceId, "serving.instanceId"));
|
|
558
|
+
const gate = await serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId }).observe();
|
|
559
|
+
return { verdict, ...(gate ? { processEpoch: gate.processEpoch } : {}) };
|
|
560
|
+
},
|
|
561
|
+
}, principal, claim);
|
|
562
|
+
},
|
|
549
563
|
retireInteractiveLifecycle: async (args) => {
|
|
550
564
|
refuseIfFenced();
|
|
551
565
|
const owner = assertDerivedOwnerToken(args.owner);
|
|
@@ -604,13 +618,12 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
604
618
|
await flight;
|
|
605
619
|
return { retired: true, lifecycleUid };
|
|
606
620
|
},
|
|
607
|
-
issueManagerServiceAuthority: async (
|
|
621
|
+
issueManagerServiceAuthority: async (holder) => {
|
|
608
622
|
refuseIfFenced();
|
|
623
|
+
const { owner } = holder;
|
|
609
624
|
const observeManagerGate = async (instanceId) => (await serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId }).observe()) ?? null;
|
|
610
625
|
return issueRemoteManagerAuthority({
|
|
611
|
-
|
|
612
|
-
scope,
|
|
613
|
-
request,
|
|
626
|
+
...holder,
|
|
614
627
|
authorizeRenewal: ({ owner: o, request: r }) => authorizeRemoteManagerRenewal({
|
|
615
628
|
request: r, owner: o, space, accountPublicKey: dataAccount.pub, proofSecret: dataAccount.signingSeed,
|
|
616
629
|
observeManagerGate,
|
|
@@ -808,14 +821,13 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
808
821
|
},
|
|
809
822
|
});
|
|
810
823
|
},
|
|
811
|
-
maintainRemoteManager: async (
|
|
824
|
+
maintainRemoteManager: async (holder) => {
|
|
812
825
|
refuseIfFenced();
|
|
826
|
+
const { owner } = holder;
|
|
813
827
|
const gateKv = await new Kvm(remoteIssuer.nc).open(epAuthBucket(space));
|
|
814
828
|
const authorized = await authorizeRemoteManagerMaintenance({
|
|
815
|
-
|
|
816
|
-
scope,
|
|
829
|
+
...holder,
|
|
817
830
|
space,
|
|
818
|
-
request,
|
|
819
831
|
scanner,
|
|
820
832
|
observeManagerGate: async (instanceId) => serveIssuanceGateKv(gateKv, space, { endpoint: "manager", instanceId }).observe(),
|
|
821
833
|
});
|
|
@@ -854,7 +866,7 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
854
866
|
const result = await principalOracle(principal);
|
|
855
867
|
return { state: result.state, detail: result.note ?? `delivery-daemon principal sweep, sweepComplete=${String(result.sweepComplete)}` };
|
|
856
868
|
},
|
|
857
|
-
|
|
869
|
+
evictHolders: makeDeliveryAdminHolderEvictor({ space, server, dataAccount, log }),
|
|
858
870
|
log,
|
|
859
871
|
});
|
|
860
872
|
}
|
|
@@ -863,14 +875,12 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
863
875
|
}
|
|
864
876
|
return completeRemoteManagerMaintenance(authorized, owner, { reconciliation: report });
|
|
865
877
|
},
|
|
866
|
-
validateRetainedAgent: async (
|
|
878
|
+
validateRetainedAgent: async (holder) => {
|
|
867
879
|
refuseIfFenced();
|
|
868
880
|
return authorizeRemoteRetainedAgentValidation({
|
|
869
|
-
|
|
870
|
-
scope,
|
|
881
|
+
...holder,
|
|
871
882
|
proofSecret: dataAccount.signingSeed,
|
|
872
883
|
space,
|
|
873
|
-
request,
|
|
874
884
|
observeManagerGate: async (instanceId) => {
|
|
875
885
|
const gate = serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId });
|
|
876
886
|
return gate.observe();
|
|
@@ -905,10 +915,27 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
905
915
|
},
|
|
906
916
|
});
|
|
907
917
|
},
|
|
908
|
-
|
|
918
|
+
verifyManagedAgentRuntime: async ({ owner, request }) => {
|
|
919
|
+
refuseIfFenced();
|
|
920
|
+
const args = {
|
|
921
|
+
owner,
|
|
922
|
+
dir: opts.dir,
|
|
923
|
+
proofSecret: dataAccount.signingSeed,
|
|
924
|
+
space,
|
|
925
|
+
observeManagerGate: async (instanceId) => {
|
|
926
|
+
const gate = serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId });
|
|
927
|
+
return gate.observe();
|
|
928
|
+
},
|
|
929
|
+
};
|
|
930
|
+
return request.kind === "manager-managed-agent-runtime-status"
|
|
931
|
+
? authorizeRemoteManagedAgentRuntimeStatus({ ...args, request })
|
|
932
|
+
: authorizeRemoteManagedAgentRuntimeCreate({ ...args, request });
|
|
933
|
+
},
|
|
934
|
+
scanManagerGoalIndex: async (holder) => {
|
|
909
935
|
refuseIfFenced();
|
|
936
|
+
const { owner } = holder;
|
|
910
937
|
const authorized = await authorizeRemoteManagerGoalIndexScan({
|
|
911
|
-
|
|
938
|
+
...holder, proofSecret: dataAccount.signingSeed, space,
|
|
912
939
|
observeManagerGate: async (instanceId) => {
|
|
913
940
|
const gate = serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId });
|
|
914
941
|
return gate.observe();
|
|
@@ -916,11 +943,11 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
916
943
|
});
|
|
917
944
|
return completeRemoteManagerGoalIndexScan(authorized, owner, await recordsScanner.scanManagerGoalIndex(owner));
|
|
918
945
|
},
|
|
919
|
-
authorizeManagerAdmin: async ({
|
|
946
|
+
authorizeManagerAdmin: async ({ request, ...holder }) => {
|
|
920
947
|
refuseIfFenced();
|
|
921
948
|
return authorizeRemoteManagerAdmin({
|
|
922
|
-
managerOwner: owner,
|
|
923
|
-
managerScope: scope,
|
|
949
|
+
managerOwner: holder.owner,
|
|
950
|
+
...(holder.holder === "platform" ? { managerAssignment: holder.assignment } : { managerScope: holder.scope }),
|
|
924
951
|
proofSecret: dataAccount.signingSeed,
|
|
925
952
|
space,
|
|
926
953
|
dir: opts.dir,
|
|
@@ -931,10 +958,10 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
931
958
|
},
|
|
932
959
|
});
|
|
933
960
|
},
|
|
934
|
-
admitManagerRun: async ({
|
|
961
|
+
admitManagerRun: async ({ request, ...holder }) => {
|
|
935
962
|
refuseIfFenced();
|
|
936
|
-
|
|
937
|
-
|
|
963
|
+
const { owner } = holder;
|
|
964
|
+
requireManagerAuthorityHolder(holder, String(request?.instanceId), 'manager run admission needs scope "supervise"; spawn/admin do not imply it');
|
|
938
965
|
const runId = parseRemoteRunAdmissionRequest(request).run.runId;
|
|
939
966
|
const hostAuth = {
|
|
940
967
|
space,
|
|
@@ -968,10 +995,10 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
968
995
|
}
|
|
969
996
|
});
|
|
970
997
|
},
|
|
971
|
-
issueManagerRunAttempt: async ({
|
|
998
|
+
issueManagerRunAttempt: async ({ request, ...holder }) => {
|
|
972
999
|
refuseIfFenced();
|
|
973
|
-
|
|
974
|
-
|
|
1000
|
+
const { owner } = holder;
|
|
1001
|
+
requireManagerAuthorityHolder(holder, String(request?.instanceId), 'manager run attempt needs scope "supervise"; spawn/admin do not imply it');
|
|
975
1002
|
const req = parseRemoteRunAttemptRequest(request);
|
|
976
1003
|
const recordsKv = await openRecordsBucket(remoteIssuer.nc, space);
|
|
977
1004
|
const grant = await authorizeRemoteRunAttempt({
|
|
@@ -1049,6 +1076,12 @@ export async function openAuthAuthorityPlane(opts) {
|
|
|
1049
1076
|
credentials: { operator },
|
|
1050
1077
|
};
|
|
1051
1078
|
},
|
|
1079
|
+
observeManagerInstance: async (instanceId) => {
|
|
1080
|
+
refuseIfFenced();
|
|
1081
|
+
const gate = await serveIssuanceGateKv(await new Kvm(remoteIssuer.nc).open(epAuthBucket(space)), space, { endpoint: "manager", instanceId }).observe();
|
|
1082
|
+
const rec = await readSvcRecordLeader(recordsJsm, space, recordSpecKey(RECORD_KINDS.svc, ["manager", instanceId]));
|
|
1083
|
+
return { registered: rec !== undefined && !("deleted" in rec), gate: gate ?? null };
|
|
1084
|
+
},
|
|
1052
1085
|
fenced,
|
|
1053
1086
|
close: async () => {
|
|
1054
1087
|
// Clean-close order (SPEC 13.13): the rail stops answering first, then scan-capable
|
|
@@ -1259,6 +1292,8 @@ export async function startAuthService(inputs) {
|
|
|
1259
1292
|
const port = inputs.port ?? 0;
|
|
1260
1293
|
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
1261
1294
|
throw new Error(`auth-service: port must be a port number, got ${port}`);
|
|
1295
|
+
if (inputs.platformControl !== undefined && typeof inputs.platformControl?.observeAssignment !== "function")
|
|
1296
|
+
throw new Error("auth-service: platformControl requires an observeAssignment function");
|
|
1262
1297
|
const started = await startAuthContext({
|
|
1263
1298
|
space: inputs.space,
|
|
1264
1299
|
server: inputs.servers,
|
|
@@ -1268,6 +1303,8 @@ export async function startAuthService(inputs) {
|
|
|
1268
1303
|
hostedStore: true,
|
|
1269
1304
|
localManager: () => undefined,
|
|
1270
1305
|
context: inputs.context,
|
|
1306
|
+
...(inputs.platformControl !== undefined ? { platformControl: inputs.platformControl } : {}),
|
|
1307
|
+
...(inputs.standingRenewableTtlSeconds !== undefined ? { standingRenewableTtlSeconds: inputs.standingRenewableTtlSeconds } : {}),
|
|
1271
1308
|
});
|
|
1272
1309
|
return started.handle;
|
|
1273
1310
|
}
|
|
@@ -1303,6 +1340,7 @@ async function startAuthContext(o) {
|
|
|
1303
1340
|
dataAccount: { pub: keys.dataAccount.pub, signingSeed: keys.dataAccount.signingSeed },
|
|
1304
1341
|
log: (l) => console.error(l),
|
|
1305
1342
|
localManager: o.localManager,
|
|
1343
|
+
...(o.standingRenewableTtlSeconds !== undefined ? { standingRenewableTtlSeconds: o.standingRenewableTtlSeconds } : {}),
|
|
1306
1344
|
});
|
|
1307
1345
|
let nc;
|
|
1308
1346
|
let http;
|
|
@@ -1333,7 +1371,7 @@ async function startAuthContext(o) {
|
|
|
1333
1371
|
space,
|
|
1334
1372
|
token: { key: issuer.localKeySet(), issuer: issuer.issuer },
|
|
1335
1373
|
authorizeActor: plane.authorizeConnect,
|
|
1336
|
-
permissionsFor: calloutPermissions(ledgerAclResolver(dir), plane.authorizeManagerCaller),
|
|
1374
|
+
permissionsFor: calloutPermissions(ledgerAclResolver(dir), plane.authorizeManagerCaller, plane.verifySession),
|
|
1337
1375
|
log: (l) => console.error(l),
|
|
1338
1376
|
});
|
|
1339
1377
|
// The subscription must be ON the broker before readiness is signaled — an `up` that recorded a
|
|
@@ -1362,6 +1400,7 @@ async function startAuthContext(o) {
|
|
|
1362
1400
|
validateRetainedAgent: plane.validateRetainedAgent,
|
|
1363
1401
|
verifyManagedAgentEnrollment: plane.verifyManagedAgentEnrollment,
|
|
1364
1402
|
verifyManagedAgentPrepareRetirement: plane.verifyManagedAgentPrepareRetirement,
|
|
1403
|
+
verifyManagedAgentRuntime: plane.verifyManagedAgentRuntime,
|
|
1365
1404
|
scanManagerGoalIndex: plane.scanManagerGoalIndex,
|
|
1366
1405
|
authorizeManagerAdmin: plane.authorizeManagerAdmin,
|
|
1367
1406
|
admitManagerRun: plane.admitManagerRun,
|
|
@@ -1376,6 +1415,7 @@ async function startAuthContext(o) {
|
|
|
1376
1415
|
dir,
|
|
1377
1416
|
mintConnectCredential: plane.mintConnectCredential,
|
|
1378
1417
|
selectManagerInstance: plane.selectManagerInstance,
|
|
1418
|
+
verifySession: plane.verifySession,
|
|
1379
1419
|
};
|
|
1380
1420
|
const loopback = createServer((req, res) => void handle(req, res, ctx));
|
|
1381
1421
|
http = loopback;
|
|
@@ -1445,9 +1485,46 @@ async function startAuthContext(o) {
|
|
|
1445
1485
|
return end;
|
|
1446
1486
|
});
|
|
1447
1487
|
const context = o.context ?? { accountPublicKey: keys.dataAccount.pub, lifecycleUid: "" };
|
|
1488
|
+
const platformControl = o.platformControl;
|
|
1489
|
+
// The human route's retained-validation composition, for the platform holder.
|
|
1490
|
+
const validateRetainedForHolder = async (holder, request) => {
|
|
1491
|
+
const retained = await plane.validateRetainedAgent({ ...holder, request });
|
|
1492
|
+
const authority = await validateRetainedManagedAgent({
|
|
1493
|
+
store: secrets,
|
|
1494
|
+
dir,
|
|
1495
|
+
space,
|
|
1496
|
+
owner: retained.target.owner,
|
|
1497
|
+
actor: retained.target.actor,
|
|
1498
|
+
actorToken: retained.actorToken,
|
|
1499
|
+
sentinelCreds: retained.sentinelCreds,
|
|
1500
|
+
});
|
|
1501
|
+
return completeRemoteRetainedAgentValidation(retained, holder.owner, authority);
|
|
1502
|
+
};
|
|
1448
1503
|
const service = {
|
|
1449
1504
|
url,
|
|
1450
1505
|
...(publicUrl !== undefined ? { publicUrl } : {}),
|
|
1506
|
+
...(platformControl !== undefined ? {
|
|
1507
|
+
platformControlAuthority: makePlatformControlAuthority({
|
|
1508
|
+
space,
|
|
1509
|
+
accountPublicKey: keys.dataAccount.pub,
|
|
1510
|
+
owner: platformControlOwner(ownerSecret, space, keys.dataAccount.pub),
|
|
1511
|
+
observeAssignment: (s, a) => platformControl.observeAssignment(s, a),
|
|
1512
|
+
observeManagerInstance: plane.observeManagerInstance,
|
|
1513
|
+
dispatch: (holder, request) => {
|
|
1514
|
+
if (state !== "ready" || closePromise !== undefined)
|
|
1515
|
+
throw new EpEnvelopeError("unavailable", "auth-service context is not ready");
|
|
1516
|
+
switch (request.kind) {
|
|
1517
|
+
case "manager-service-authority": return plane.issueManagerServiceAuthority({ ...holder, request });
|
|
1518
|
+
case "manager-service-maintenance": return plane.maintainRemoteManager({ ...holder, request });
|
|
1519
|
+
case "manager-goal-index-scan": return plane.scanManagerGoalIndex({ ...holder, request });
|
|
1520
|
+
case "manager-admin-authorization": return plane.authorizeManagerAdmin({ ...holder, request });
|
|
1521
|
+
case "manager-run-admission": return plane.admitManagerRun({ ...holder, request });
|
|
1522
|
+
case "manager-run-attempt": return plane.issueManagerRunAttempt({ ...holder, request });
|
|
1523
|
+
case "manager-retained-agent-validation": return validateRetainedForHolder(holder, request);
|
|
1524
|
+
}
|
|
1525
|
+
},
|
|
1526
|
+
}),
|
|
1527
|
+
} : {}),
|
|
1451
1528
|
readiness() {
|
|
1452
1529
|
if (state === "unavailable")
|
|
1453
1530
|
return { state, context, cause: cause ?? "auth-service context is unavailable" };
|
|
@@ -1482,6 +1559,9 @@ export async function dispatchManagerAuthorityRequest(ctx, owner, body) {
|
|
|
1482
1559
|
// would answer a manager-lifecycle phase for an agent-lifecycle request.
|
|
1483
1560
|
if (request.kind === "manager-managed-agent-enrollment" || request.kind === "manager-managed-agent-prepare-retirement")
|
|
1484
1561
|
throw new EpEnvelopeError("unimplemented", "managed agent enrollment and retirement preparation must be handled by host platform interception");
|
|
1562
|
+
// The hosted runtime kinds read and drive host-owned intent state that stock does not hold.
|
|
1563
|
+
if (request.kind === "manager-managed-agent-runtime-create" || request.kind === "manager-managed-agent-runtime-status")
|
|
1564
|
+
throw new EpEnvelopeError("unimplemented", "managed agent runtime create and status must be handled by host platform interception");
|
|
1485
1565
|
if (request.kind === "manager-retained-agent-validation") {
|
|
1486
1566
|
const retained = await ctx.validateRetainedAgent({
|
|
1487
1567
|
owner,
|
|
@@ -1731,8 +1811,9 @@ async function handleVerifyManagedAgentEnrollment(req, res, ctx) {
|
|
|
1731
1811
|
if (typeof raw.owner !== "string" || raw.request === null || typeof raw.request !== "object" || Array.isArray(raw.request))
|
|
1732
1812
|
return send(res, 400, { error: "managed agent enrollment verification needs a string owner and an object request" });
|
|
1733
1813
|
const request = raw.request;
|
|
1734
|
-
|
|
1735
|
-
|
|
1814
|
+
const runtimeKind = request.kind === "manager-managed-agent-runtime-create" || request.kind === "manager-managed-agent-runtime-status";
|
|
1815
|
+
if (request.kind !== "manager-managed-agent-enrollment" && request.kind !== "manager-managed-agent-prepare-retirement" && !runtimeKind)
|
|
1816
|
+
return send(res, 400, { error: 'managed agent enrollment verification serves only kind "manager-managed-agent-enrollment", "manager-managed-agent-prepare-retirement", "manager-managed-agent-runtime-create" or "manager-managed-agent-runtime-status"' });
|
|
1736
1817
|
if (typeof request.actor !== "string")
|
|
1737
1818
|
return send(res, 400, { error: "managed agent enrollment verification request requires an actor" });
|
|
1738
1819
|
try {
|
|
@@ -1742,6 +1823,14 @@ async function handleVerifyManagedAgentEnrollment(req, res, ctx) {
|
|
|
1742
1823
|
// caller. An ungranted actor throws the ledger's own operator-exact sentence.
|
|
1743
1824
|
const row = ledgerAuthorizeGrant(ctx.dir)(owner, request.actor);
|
|
1744
1825
|
const scope = row.scope ?? [];
|
|
1826
|
+
if (runtimeKind) {
|
|
1827
|
+
// The plane re-reads the same ledger row itself: the runtime decision never takes a scope.
|
|
1828
|
+
const decision = await ctx.verifyManagedAgentRuntime({
|
|
1829
|
+
owner,
|
|
1830
|
+
request: raw.request,
|
|
1831
|
+
});
|
|
1832
|
+
return send(res, 200, { authorized: true, ...decision });
|
|
1833
|
+
}
|
|
1745
1834
|
if (request.kind === "manager-managed-agent-enrollment") {
|
|
1746
1835
|
const verified = await ctx.verifyManagedAgentEnrollment({
|
|
1747
1836
|
owner,
|
|
@@ -1819,7 +1908,9 @@ async function handleExchange(req, res, ctx, policy) {
|
|
|
1819
1908
|
const peer = policy.peerKey(req);
|
|
1820
1909
|
const peerThrottled = policy.throttled(ctx, peer);
|
|
1821
1910
|
const body = await readJsonBody(req);
|
|
1822
|
-
const { idpToken, actor, actorToken, owner, ttlSec, view, managerInstanceId } = body;
|
|
1911
|
+
const { idpToken, actor, actorToken, owner, ttlSec, view, managerInstanceId, sessionGrant } = body;
|
|
1912
|
+
if ((view === "session-caller") !== (sessionGrant !== undefined))
|
|
1913
|
+
return send(res, 400, { error: 'view "session-caller" and sessionGrant come together or not at all' });
|
|
1823
1914
|
if (ttlSec !== undefined && typeof ttlSec !== "number")
|
|
1824
1915
|
return send(res, 400, { error: "ttlSec must be a number" });
|
|
1825
1916
|
if (view !== undefined && typeof view !== "string")
|
|
@@ -1898,11 +1989,16 @@ async function handleExchange(req, res, ctx, policy) {
|
|
|
1898
1989
|
const selectedManagerInstanceId = view === "manager-caller"
|
|
1899
1990
|
? await ctx.selectManagerInstance(deriveOwnerForIdpSubject(ctx.ownerSecret, ctx.bridgeIdp.issuer, (await verifyIdpToken(idpToken, ctx.bridgeIdp)).sub), managerInstanceId)
|
|
1900
1991
|
: undefined;
|
|
1992
|
+
// #2312: a session-caller bearer is signed only once the identity plane has decided that THIS
|
|
1993
|
+
// principal holds THAT redeemed session. The bridge's `verifySession` hook runs the decision on
|
|
1994
|
+
// the principal it derived, before signing; the callout re-runs it at the mint.
|
|
1995
|
+
const session = view === "session-caller" ? grantCoordinates(sessionGrant) : undefined;
|
|
1901
1996
|
const r = await ctx.bridge.exchange(idpToken, {
|
|
1902
1997
|
actor,
|
|
1903
1998
|
ttlSec,
|
|
1904
1999
|
view: view,
|
|
1905
2000
|
managerInstanceId: selectedManagerInstanceId,
|
|
2001
|
+
...(session ? { verifySession: (p) => ctx.verifySession(p, session) } : {}),
|
|
1906
2002
|
});
|
|
1907
2003
|
return send(res, 200, r);
|
|
1908
2004
|
}
|