@ory/argus 1.2.4 → 1.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 +25 -0
- package/assets/skills/ory-build-agent/SKILL.md +1 -1
- package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
- package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
- package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
- package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
- package/dist/adapters.d.ts +11 -3
- package/dist/adapters.js +55 -13
- package/dist/agent-auth.d.ts +18 -7
- package/dist/agent-auth.js +120 -10
- package/dist/auth-store.d.ts +3 -3
- package/dist/auth-store.js +56 -12
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +1 -1
- package/dist/build-info.json +4 -4
- package/dist/cli.js +5 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +24 -7
- package/dist/config.d.ts +23 -3
- package/dist/config.js +202 -18
- package/dist/contract-suite.d.ts +0 -8
- package/dist/contract-suite.js +0 -1
- package/dist/event-reporter.d.ts +1 -0
- package/dist/event-reporter.js +19 -2
- package/dist/hook-timeout.d.ts +2 -2
- package/dist/hook-timeout.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +13 -6
- package/dist/lifecycle.js +2 -1
- package/dist/local/configs.d.ts +2 -4
- package/dist/local/configs.js +2 -4
- package/dist/local/manager.js +4 -4
- package/dist/local/seed.d.ts +4 -4
- package/dist/local/seed.js +8 -10
- package/dist/runtime-credential.d.ts +35 -0
- package/dist/runtime-credential.js +252 -1
- package/dist/runtime.d.ts +19 -12
- package/dist/runtime.js +69 -21
- package/dist/setup.js +3 -3
- package/dist/skills.d.ts +3 -5
- package/dist/skills.js +14 -5
- package/dist/subject.d.ts +2 -3
- package/dist/subject.js +10 -11
- package/dist/testing.d.ts +17 -0
- package/dist/testing.js +53 -0
- package/dist/tool-catalog.d.ts +12 -12
- package/dist/tool-catalog.js +206 -50
- package/dist/user-login.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.collectSystemServices = exports.wrapTool = exports.writeAgentDelegatesSubagent = exports.writeUserDelegatesAgent = exports.registerSubagent = exports.fail = exports.complete = exports.gate = exports.decideTool = exports.withHookContext = exports.createSessionStarter = exports.sessionStart = exports.resolveNamespace = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.shellDenialFields = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.DEFAULT_USER_SUBJECT_NAMESPACE = exports.SESSION_NAMESPACE = exports.SUBAGENT_NAMESPACE = exports.AGENT_NAMESPACE = exports.resolveSubAgentSpawnSubject = exports.resolveSubAgentSubject = exports.resolveAgentSessionSubject = exports.resolveAgentSubject = exports.subjectLabel = exports.runWithUserSubject = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = exports.isToolExecutionPhase = void 0;
|
|
3
|
+
exports.generateCodeVerifier = exports.detectHeadless = exports.refreshAccessToken = exports.pkceLogin = exports.resetPermissionModeCache = exports.warmPermissionModeCache = exports.resolvePermissionMode = exports.SESSIONLESS_KEY = exports.UNKNOWN_HARNESS_KEY = exports.unregisterInstalledHarness = exports.registerInstalledHarness = exports.listInstalledHarnesses = exports.getHarnessDataDir = exports.getDataDir = exports.getConfigPath = exports.mutateConfigAsync = exports.mutateConfig = exports.AGENT_ROOT_DELEGATION_ENV_VAR = exports.validateAgentRootDelegation = exports.readAgentRootDelegation = exports.describeSecurityGap = exports.isSecurityConnected = exports.resolveConfig = exports.saveConfig = exports.loadConfig = exports.clearSessionContext = exports.setSessionContext = exports.withSessionContext = exports.currentSessionContext = exports.projectActivityEntry = exports.SERVER_ACTIVITY_EVENTS = exports.AgentSecurityEventReporter = exports.withQuietStderr = exports.formatLogEntry = exports.redactLogData = exports.ActiveActivity = exports.DebugLogger = exports.talosRuntimeCredential = exports.runCredentialStoreCommand = exports.resolveRuntimeCredential = exports.enrollTalosRuntimeCredential = exports.enrollTalosChildRuntimeCredential = exports.createOsTalosCredentialStore = exports.createSpiffeFileRequestAuthenticator = exports.configureSystemCaTrustForDefaultFetch = exports.credentialAccount = exports.canonicalProjectUrl = exports.canonicalCredentialIdentity = exports.authenticatedRuntimeRequest = exports.OryAgentClient = void 0;
|
|
4
|
+
exports.clearSubAgentDynamicCredentials = exports.saveSubAgentDynamicCredentials = exports.loadSubAgentDynamicCredentialsByHarness = exports.loadAllSubAgentDynamicCredentials = exports.loadSubAgentDynamicCredentials = exports.claimAgentDynamicCredentials = exports.countAgentSessions = exports.latestSubAgentSession = exports.latestAgentRegistration = exports.clearAgentDynamicCredentials = exports.saveAgentDynamicCredentials = exports.loadAgentDynamicCredentialsByHarness = exports.loadAgentDynamicCredentials = exports.registerAgentClient = exports.fetchClientCredentialsToken = exports.ensureSubAgentIdentity = exports.ensureAgentIdentity = exports.resolveRuntimeAgentCredentials = exports.resolvePreprovisionedRuntimeContext = exports.resolveAgentCredentials = exports.ensureAuthenticated = exports.ensureUserAuthenticated = exports.TOKEN_EXPIRY_SKEW_SEC = exports.waitForPeerTokensSync = exports.waitForPeerTokens = exports.clearPkceFlightLock = exports.tryAcquirePkceFlightLock = exports.refreshAndSave = exports.isExpired = exports.clearTokens = exports.saveTokens = exports.loadTokens = exports.toolHookTimeout = exports.sessionStartHookTimeout = exports.HOOK_TIMEOUT_MAX_MS = exports.TOOL_HOOK_WINDOW_MS = exports.SESSION_START_HOOK_WINDOW_MS = exports.describeDanglingRegistration = exports.findDanglingRegistration = exports.deregisterExternalRegistrations = exports.clearAllExternalRegistrations = exports.clearExternalRegistration = exports.recordExternalRegistration = exports.loadExternalRegistrations = exports.shouldPrintHelp = exports.wantsHelp = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.LOOPBACK_PORTS = exports.buildAuthorizeUrl = exports.sha256Base64Url = void 0;
|
|
5
|
+
exports.removeFlatHooks = exports.mergeFlatHooks = exports.flatHookEntry = exports.removeMatcherHooks = exports.mergeMatcherHooks = exports.matcherHookEntry = exports.isOryHookCommand = exports.writeJsonFile = exports.readJsonFile = exports.parseSetupArgs = exports.resolveBuildInfo = exports.collectVersionInfo = exports.runVersionCommand = exports.probePermissionsCoverage = exports.collectStatusReport = exports.printPermissionsSection = exports.printAgentIdentitySection = exports.printUserIdentitySection = exports.runStatusCommand = exports.runPostInstall = exports.runWatchCommand = exports.runPermissionsCommand = exports.oryNpx = exports.interactiveConfigPrompt = exports.promptForProjectUrl = exports.promptOnTty = exports.isTtyAvailable = exports.printEnvHelp = exports.printLogTail = exports.printEnvironment = exports.printOryConfig = exports.runAgentCommand = exports.runConfigureCommand = exports.resolveInstalledHarnesses = exports.clearCredentialsForUninstall = exports.READ_CREDENTIAL_RETRY_MS = exports.resetReadCredentialBackoff = exports.ensureReadCredential = exports.DEFAULT_SESSION_RETENTION = exports.AGENT_TOKEN_EXPIRY_SKEW_SEC = exports.revokeAgentDynamicClient = exports.sessionRetention = exports.retireAgentSessions = exports.clearRetiredCredentials = exports.loadRetiredCredentials = exports.saveDelegationNodeId = exports.resolvePreprovisionedRootDelegation = exports.loadDelegationNodeId = exports.claimSubAgentDynamicCredentials = exports.clearSubAgentDynamicCredentialsForHarness = void 0;
|
|
6
|
+
exports.ORY_COMMAND_SLUGS = exports.ORY_COMMAND_SKILL_NAMES = exports.ORY_SKILL_NAMES = exports.removeSkillDirs = exports.writeSkillTree = exports.toSkillMarkdown = exports.commandToPlainMarkdown = exports.commandToFrontmatterMarkdown = exports.commandToToml = exports.commandToSkill = exports.renderOryCommands = exports.renderOrySkills = exports.renderMirrorBootstrap = exports.MCP_SERVER_PACKAGE = exports.defaultNpmInstaller = exports.describeRuntimeHealth = exports.checkRuntimeHealth = exports.pruneRuntimeStores = exports.removeRuntimeWiring = exports.getRuntimeWiring = exports.recordRuntimeWiring = exports.readRuntimeManifest = exports.getMcpShimPath = exports.getHookShimPath = exports.getShimDir = exports.getRuntimeStoreDir = exports.getRuntimeRoot = exports.moduleSpecifier = exports.requireHookCommand = exports.isOryRuntimeCommand = exports.mcpCommand = exports.hookCommand = exports.writeMcpShim = exports.writeHookShim = exports.resolveMcpRuntimeEntry = exports.resolveRuntimeEntry = exports.linkedRuntime = exports.materializeRuntime = exports.resolveRuntimeForInstall = exports.wireRuntime = exports.unregisterPlugin = exports.registerPlugin = exports.removeMcpServer = exports.mergeMcpServer = exports.mcpServerEntry = exports.emitDeferredNextSteps = exports.beginDeferNextSteps = exports.nextStepsSink = exports.printNextSteps = exports.printSetupHelp = void 0;
|
|
7
|
+
exports.getShellToolCatalog = exports.SHELL_TOOL_CATALOG = exports.isInteractiveTool = exports.getInteractiveToolCatalog = exports.INTERACTIVE_TOOL_CATALOG = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.DEFAULT_SHELL_NAMESPACE = exports.DEFAULT_NAMESPACE = exports.PERMIT_USE = exports.ENROLLMENT_NAMESPACE = exports.AGENT_ROLE_NAMESPACE = exports.DELEGATION_NAMESPACE = exports.PERMISSION_MODE_NAMESPACE = exports.PROJECT_POSTURE_OBJECT = exports.AUTO_ENROLL_OBJECT = exports.PERMISSION_MODE_OBJECT = exports.RELATION_OPTED_OUT_SUBJECTS = exports.RELATION_MEMBERS = exports.RELATION_ENFORCED_SUBJECTS = exports.RELATION_CREDENTIALS = exports.RELATION_DELEGATES = exports.RELATION_BLOCKED_SUBJECTS = exports.RELATION_USERS = exports.SUBAGENT_NODE_NAMESPACE = exports.AGENT_NODE_NAMESPACE = exports.NODE_REFERENCE_NAMESPACES = exports.PRINCIPAL_NAMESPACES = exports.OPL_IMPORT_SYMBOLS = exports.OPL_IMPORT_MODULE = exports.renderOplImport = exports.renderPrincipalClass = exports.renderEnrollmentNamespaceClass = exports.renderAgentRoleNamespaceClass = exports.renderPermissionModeNamespaceClass = exports.renderDelegationNamespaceClass = exports.renderToolNamespaceClass = exports.buildAgentSecurityOpl = exports.extractBashCommandWords = exports.extractShellCommands = exports.resolveCheckRelation = exports.decomposeAndCheck = exports.gateToolCall = exports.applyPermissionMode = exports.checkAndDecide = exports.REGISTRY_URL = exports.runRegistryCommand = exports.runLocalCommand = void 0;
|
|
8
|
+
exports.collectSystemServices = exports.wrapTool = exports.writeAgentDelegatesSubagent = exports.writeUserDelegatesAgent = exports.registerSubagent = exports.fail = exports.complete = exports.gate = exports.decideTool = exports.withHookContext = exports.createSessionStarter = exports.sessionStart = exports.resolveNamespace = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.shellDenialFields = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.DEFAULT_USER_SUBJECT_NAMESPACE = exports.SESSION_NAMESPACE = exports.SUBAGENT_NAMESPACE = exports.AGENT_NAMESPACE = exports.resolveSubAgentSpawnSubject = exports.resolveSubAgentSubject = exports.resolveAgentSessionSubject = exports.resolveAgentSubject = exports.subjectLabel = exports.runWithUserSubject = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = exports.isToolExecutionPhase = exports.isUserFacingPhase = exports.classifyLifecycle = exports.extractShellCommand = exports.resolveShellCommandNamespace = exports.getShellCommandCatalog = exports.SHELL_COMMAND_CATALOG = exports.isShellTool = void 0;
|
|
9
9
|
var client_js_1 = require("./client.js");
|
|
10
10
|
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return client_js_1.OryAgentClient; } });
|
|
11
11
|
var runtime_credential_js_1 = require("./runtime-credential.js");
|
|
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "canonicalCredentialIdentity", { enumerable: true
|
|
|
14
14
|
Object.defineProperty(exports, "canonicalProjectUrl", { enumerable: true, get: function () { return runtime_credential_js_1.canonicalProjectUrl; } });
|
|
15
15
|
Object.defineProperty(exports, "credentialAccount", { enumerable: true, get: function () { return runtime_credential_js_1.credentialAccount; } });
|
|
16
16
|
Object.defineProperty(exports, "configureSystemCaTrustForDefaultFetch", { enumerable: true, get: function () { return runtime_credential_js_1.configureSystemCaTrustForDefaultFetch; } });
|
|
17
|
+
Object.defineProperty(exports, "createSpiffeFileRequestAuthenticator", { enumerable: true, get: function () { return runtime_credential_js_1.createSpiffeFileRequestAuthenticator; } });
|
|
17
18
|
Object.defineProperty(exports, "createOsTalosCredentialStore", { enumerable: true, get: function () { return runtime_credential_js_1.createOsTalosCredentialStore; } });
|
|
18
19
|
Object.defineProperty(exports, "enrollTalosChildRuntimeCredential", { enumerable: true, get: function () { return runtime_credential_js_1.enrollTalosChildRuntimeCredential; } });
|
|
19
20
|
Object.defineProperty(exports, "enrollTalosRuntimeCredential", { enumerable: true, get: function () { return runtime_credential_js_1.enrollTalosRuntimeCredential; } });
|
|
@@ -41,7 +42,11 @@ Object.defineProperty(exports, "saveConfig", { enumerable: true, get: function (
|
|
|
41
42
|
Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return config_js_1.resolveConfig; } });
|
|
42
43
|
Object.defineProperty(exports, "isSecurityConnected", { enumerable: true, get: function () { return config_js_1.isSecurityConnected; } });
|
|
43
44
|
Object.defineProperty(exports, "describeSecurityGap", { enumerable: true, get: function () { return config_js_1.describeSecurityGap; } });
|
|
45
|
+
Object.defineProperty(exports, "readAgentRootDelegation", { enumerable: true, get: function () { return config_js_1.readAgentRootDelegation; } });
|
|
46
|
+
Object.defineProperty(exports, "validateAgentRootDelegation", { enumerable: true, get: function () { return config_js_1.validateAgentRootDelegation; } });
|
|
47
|
+
Object.defineProperty(exports, "AGENT_ROOT_DELEGATION_ENV_VAR", { enumerable: true, get: function () { return config_js_1.AGENT_ROOT_DELEGATION_ENV_VAR; } });
|
|
44
48
|
Object.defineProperty(exports, "mutateConfig", { enumerable: true, get: function () { return config_js_1.mutateConfig; } });
|
|
49
|
+
Object.defineProperty(exports, "mutateConfigAsync", { enumerable: true, get: function () { return config_js_1.mutateConfigAsync; } });
|
|
45
50
|
Object.defineProperty(exports, "getConfigPath", { enumerable: true, get: function () { return config_js_1.getConfigPath; } });
|
|
46
51
|
Object.defineProperty(exports, "getDataDir", { enumerable: true, get: function () { return config_js_1.getDataDir; } });
|
|
47
52
|
Object.defineProperty(exports, "getHarnessDataDir", { enumerable: true, get: function () { return config_js_1.getHarnessDataDir; } });
|
|
@@ -96,6 +101,7 @@ Object.defineProperty(exports, "ensureUserAuthenticated", { enumerable: true, ge
|
|
|
96
101
|
Object.defineProperty(exports, "ensureAuthenticated", { enumerable: true, get: function () { return user_login_js_1.ensureAuthenticated; } });
|
|
97
102
|
var agent_auth_js_1 = require("./agent-auth.js");
|
|
98
103
|
Object.defineProperty(exports, "resolveAgentCredentials", { enumerable: true, get: function () { return agent_auth_js_1.resolveAgentCredentials; } });
|
|
104
|
+
Object.defineProperty(exports, "resolvePreprovisionedRuntimeContext", { enumerable: true, get: function () { return agent_auth_js_1.resolvePreprovisionedRuntimeContext; } });
|
|
99
105
|
Object.defineProperty(exports, "resolveRuntimeAgentCredentials", { enumerable: true, get: function () { return agent_auth_js_1.resolveRuntimeAgentCredentials; } });
|
|
100
106
|
Object.defineProperty(exports, "ensureAgentIdentity", { enumerable: true, get: function () { return agent_auth_js_1.ensureAgentIdentity; } });
|
|
101
107
|
Object.defineProperty(exports, "ensureSubAgentIdentity", { enumerable: true, get: function () { return agent_auth_js_1.ensureSubAgentIdentity; } });
|
|
@@ -117,6 +123,7 @@ Object.defineProperty(exports, "clearSubAgentDynamicCredentials", { enumerable:
|
|
|
117
123
|
Object.defineProperty(exports, "clearSubAgentDynamicCredentialsForHarness", { enumerable: true, get: function () { return agent_auth_js_1.clearSubAgentDynamicCredentialsForHarness; } });
|
|
118
124
|
Object.defineProperty(exports, "claimSubAgentDynamicCredentials", { enumerable: true, get: function () { return agent_auth_js_1.claimSubAgentDynamicCredentials; } });
|
|
119
125
|
Object.defineProperty(exports, "loadDelegationNodeId", { enumerable: true, get: function () { return agent_auth_js_1.loadDelegationNodeId; } });
|
|
126
|
+
Object.defineProperty(exports, "resolvePreprovisionedRootDelegation", { enumerable: true, get: function () { return agent_auth_js_1.resolvePreprovisionedRootDelegation; } });
|
|
120
127
|
Object.defineProperty(exports, "saveDelegationNodeId", { enumerable: true, get: function () { return agent_auth_js_1.saveDelegationNodeId; } });
|
|
121
128
|
Object.defineProperty(exports, "loadRetiredCredentials", { enumerable: true, get: function () { return agent_auth_js_1.loadRetiredCredentials; } });
|
|
122
129
|
Object.defineProperty(exports, "clearRetiredCredentials", { enumerable: true, get: function () { return agent_auth_js_1.clearRetiredCredentials; } });
|
package/dist/lifecycle.js
CHANGED
|
@@ -160,7 +160,8 @@ exports.HARNESS_LIFECYCLE_MAP = {
|
|
|
160
160
|
},
|
|
161
161
|
// Cursor (editor + CLI). Subprocess hooks read from `.cursor/hooks.json`.
|
|
162
162
|
// `preToolUse` is the single permission gate — it already fires for every
|
|
163
|
-
// tool category (Shell, Read, Write,
|
|
163
|
+
// documented tool category (Shell, Read, Write, Grep, Delete, Task) and
|
|
164
|
+
// dynamic MCP calls. The more specific
|
|
164
165
|
// `beforeShellExecution` / `beforeMCPExecution` / `beforeReadFile` hooks and
|
|
165
166
|
// their `after*` counterparts surface activity to the user and are
|
|
166
167
|
// activity-only (`user.interaction`), so shell/MCP/file access isn't gated
|
package/dist/local/configs.d.ts
CHANGED
|
@@ -76,10 +76,8 @@ export interface OathkeeperConfig {
|
|
|
76
76
|
* contract: an Oathkeeper service configured like the hosted
|
|
77
77
|
* `ory-corp:cloud:keto:read:user-token` rule — `oauth2_introspection`
|
|
78
78
|
* authenticator against Hydra, `allow` authorizer, `noop` mutator, `strip_path:/user`
|
|
79
|
-
* upstream to keto-read — so a valid
|
|
80
|
-
* read
|
|
81
|
-
* plugin's agent DCR / user PKCE tokens are populated before any permission read
|
|
82
|
-
* runs, so this is transparent in the normal flow.)
|
|
79
|
+
* upstream to keto-read — so a valid OAuth2 access token is *required* to use
|
|
80
|
+
* this direct local read path, exactly as it is on a hosted Ory project.
|
|
83
81
|
*
|
|
84
82
|
* Set `ORY_LOCAL_OATHKEEPER=0` (also `false`/`no`/`off`) to opt out and fall back
|
|
85
83
|
* to a transparent nginx passthrough that strips `/user` and forwards straight to
|
package/dist/local/configs.js
CHANGED
|
@@ -201,10 +201,8 @@ function getConsoleConfig(projectRoot) {
|
|
|
201
201
|
* contract: an Oathkeeper service configured like the hosted
|
|
202
202
|
* `ory-corp:cloud:keto:read:user-token` rule — `oauth2_introspection`
|
|
203
203
|
* authenticator against Hydra, `allow` authorizer, `noop` mutator, `strip_path:/user`
|
|
204
|
-
* upstream to keto-read — so a valid
|
|
205
|
-
* read
|
|
206
|
-
* plugin's agent DCR / user PKCE tokens are populated before any permission read
|
|
207
|
-
* runs, so this is transparent in the normal flow.)
|
|
204
|
+
* upstream to keto-read — so a valid OAuth2 access token is *required* to use
|
|
205
|
+
* this direct local read path, exactly as it is on a hosted Ory project.
|
|
208
206
|
*
|
|
209
207
|
* Set `ORY_LOCAL_OATHKEEPER=0` (also `false`/`no`/`off`) to opt out and fall back
|
|
210
208
|
* to a transparent nginx passthrough that strips `/user` and forwards straight to
|
package/dist/local/manager.js
CHANGED
|
@@ -843,7 +843,7 @@ function printSeedResult(result) {
|
|
|
843
843
|
console.log("Seed complete!");
|
|
844
844
|
console.log("==============");
|
|
845
845
|
console.log("");
|
|
846
|
-
console.log(` Agent identity:
|
|
846
|
+
console.log(` Agent identity: Talos enrollment runs at session start (nothing seeded)`);
|
|
847
847
|
console.log("");
|
|
848
848
|
console.log(` User identity: ${result.user.identity.email} (${result.user.identity.id})`);
|
|
849
849
|
console.log(` User password: ${result.user.password}`);
|
|
@@ -860,9 +860,9 @@ function printSeedResult(result) {
|
|
|
860
860
|
console.log(` export ORY_USER_SUBJECT_ID=${result.user.identity.id}`);
|
|
861
861
|
console.log("");
|
|
862
862
|
console.log("The user gate runs PKCE in the browser — sign in with the email and");
|
|
863
|
-
console.log("password printed above.
|
|
864
|
-
console.log("
|
|
865
|
-
console.log("
|
|
863
|
+
console.log("password printed above. At first session start, the authenticated user");
|
|
864
|
+
console.log("enrolls an actor-bound Talos credential. It is stored in the native OS");
|
|
865
|
+
console.log("credential store; tool hooks only load the existing credential.");
|
|
866
866
|
console.log("");
|
|
867
867
|
console.log("Or save the project URL to the shared config (persists across sessions):");
|
|
868
868
|
console.log("");
|
package/dist/local/seed.d.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* redirect URIs. The launcher always runs the real PKCE browser flow; the
|
|
8
8
|
* user types these credentials into the login UI to complete the flow.
|
|
9
9
|
*
|
|
10
|
-
* No agent principal is seeded:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* No agent principal is seeded: Agent Security enrolls a Talos runtime
|
|
11
|
+
* credential at session start using the authenticated user's bearer, and
|
|
12
|
+
* permission checks are made against the user subject. A pre-seeded agent
|
|
13
|
+
* Kratos identity would be unused.
|
|
14
14
|
*
|
|
15
15
|
* Permission tuples are written as SubjectSet `User:<identity.id>` so
|
|
16
16
|
* the Console's *Add relationship* dialog (which always emits SubjectSet
|
package/dist/local/seed.js
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* redirect URIs. The launcher always runs the real PKCE browser flow; the
|
|
9
9
|
* user types these credentials into the login UI to complete the flow.
|
|
10
10
|
*
|
|
11
|
-
* No agent principal is seeded:
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* No agent principal is seeded: Agent Security enrolls a Talos runtime
|
|
12
|
+
* credential at session start using the authenticated user's bearer, and
|
|
13
|
+
* permission checks are made against the user subject. A pre-seeded agent
|
|
14
|
+
* Kratos identity would be unused.
|
|
15
15
|
*
|
|
16
16
|
* Permission tuples are written as SubjectSet `User:<identity.id>` so
|
|
17
17
|
* the Console's *Add relationship* dialog (which always emits SubjectSet
|
|
@@ -157,9 +157,8 @@ async function seedLocalEnvironment(namespace = "AgentTool") {
|
|
|
157
157
|
throw new Error(`backend services not ready before seed: ${readiness.pending.join(", ")} ` +
|
|
158
158
|
`did not report ready within the timeout. The stack may still be starting — retry in a moment.`);
|
|
159
159
|
}
|
|
160
|
-
// 1. User identity. No agent identity is seeded
|
|
161
|
-
//
|
|
162
|
-
// start, and permission checks target the user subject.
|
|
160
|
+
// 1. User identity. No agent identity is seeded; Talos enrollment is owned
|
|
161
|
+
// by Agent Security at session start, and checks target the user subject.
|
|
163
162
|
process.stderr.write(" Creating user identity...\n");
|
|
164
163
|
const userIdentity = await seedIdentity(USER_EMAIL, USER_PASSWORD, "Local User");
|
|
165
164
|
process.stderr.write(` User: ${userIdentity.id} (${userIdentity.email})\n`);
|
|
@@ -168,9 +167,8 @@ async function seedLocalEnvironment(namespace = "AgentTool") {
|
|
|
168
167
|
const subjectLabel = `${exports.USER_SUBJECT_NAMESPACE}:${userIdentity.id}`;
|
|
169
168
|
process.stderr.write(" Native tools: allowed unless explicitly blocked (no baseline grants)\n");
|
|
170
169
|
// 3. User OAuth2 client (PKCE) — pre-registered so the user gate's
|
|
171
|
-
// PKCE flow has somewhere to authenticate against.
|
|
172
|
-
//
|
|
173
|
-
// will self-register via DCR on first session start.
|
|
170
|
+
// PKCE flow has somewhere to authenticate against. Agent identity uses
|
|
171
|
+
// Talos runtime enrollment, so no agent OAuth2 client is seeded.
|
|
174
172
|
process.stderr.write(" Registering user OAuth2 client (PKCE)...\n");
|
|
175
173
|
let userClient = { clientId: exports.USER_CLIENT_ID };
|
|
176
174
|
try {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
2
|
+
import * as nodeHttps from "node:https";
|
|
1
3
|
/** The broker header a runtime credential must occupy for a request. */
|
|
2
4
|
export type RuntimeAuthorizationHeader = "authorization" | "x-ory-agent-authorization";
|
|
3
5
|
export interface RuntimeAuthenticatedRequest {
|
|
@@ -15,10 +17,37 @@ export interface RuntimeAuthenticatedRequest {
|
|
|
15
17
|
* the request over mTLS without exposing key material to Argus.
|
|
16
18
|
*/
|
|
17
19
|
export type RuntimeRequestAuthenticator = (request: RuntimeAuthenticatedRequest) => Promise<Response>;
|
|
20
|
+
export interface SpiffeFileRequestAuthenticatorOptions {
|
|
21
|
+
/** Atomically switched directory or symlink containing one material generation. */
|
|
22
|
+
generationPath?: string;
|
|
23
|
+
certificatePath?: string;
|
|
24
|
+
privateKeyPath?: string;
|
|
25
|
+
trustBundlePath?: string;
|
|
26
|
+
certificateFile?: string;
|
|
27
|
+
privateKeyFile?: string;
|
|
28
|
+
trustBundleFile?: string;
|
|
29
|
+
/** Optional marker read before and after the PEM files to reject mixed generations. */
|
|
30
|
+
generationFile?: string;
|
|
31
|
+
/** Exact URI SAN required on the peer leaf certificate. */
|
|
32
|
+
expectedServerSpiffeId?: string;
|
|
33
|
+
/** Exact and sole URI SAN required on the loaded client leaf certificate. */
|
|
34
|
+
expectedClientSpiffeId?: string;
|
|
35
|
+
/** Explicitly use ordinary HTTPS hostname verification instead of SPIFFE identity. */
|
|
36
|
+
verifyHostname?: boolean;
|
|
37
|
+
privateKeyPassphrase?: string;
|
|
38
|
+
/** @internal Injectable seam for focused transport tests. */
|
|
39
|
+
readFile?: typeof readFile;
|
|
40
|
+
/** @internal Injectable seam for focused generation-path tests. */
|
|
41
|
+
realpath?: typeof realpath;
|
|
42
|
+
/** @internal Injectable seam for focused transport tests. */
|
|
43
|
+
request?: typeof nodeHttps.request;
|
|
44
|
+
}
|
|
18
45
|
export type RuntimeCredentialKind = "talos" | "injected";
|
|
19
46
|
export interface RuntimeCredential {
|
|
20
47
|
kind: RuntimeCredentialKind;
|
|
21
48
|
subject?: string;
|
|
49
|
+
/** @internal Non-secret binding used to recognize the same Talos key after re-resolution. */
|
|
50
|
+
apiKeyFingerprint?: string;
|
|
22
51
|
authenticate: RuntimeRequestAuthenticator;
|
|
23
52
|
}
|
|
24
53
|
export interface ResolveRuntimeCredentialOptions {
|
|
@@ -115,6 +144,12 @@ export declare function talosRuntimeCredential(apiKey: string, options?: {
|
|
|
115
144
|
/** @internal Injectable seam for testing default-fetch TLS setup. */
|
|
116
145
|
configureSystemCaTrust?: () => void;
|
|
117
146
|
}): RuntimeCredential;
|
|
147
|
+
/**
|
|
148
|
+
* Build an mTLS authenticator from operator-managed SPIFFE files. The files are
|
|
149
|
+
* re-read for every request so atomic SVID and trust-bundle rotations are used
|
|
150
|
+
* without restarting the process.
|
|
151
|
+
*/
|
|
152
|
+
export declare function createSpiffeFileRequestAuthenticator(options: SpiffeFileRequestAuthenticatorOptions): RuntimeRequestAuthenticator;
|
|
118
153
|
/** Resolve only noninteractive runtime credentials. No persisted state is read. */
|
|
119
154
|
export declare function resolveRuntimeCredential(options?: ResolveRuntimeCredentialOptions): RuntimeCredential | undefined;
|
|
120
155
|
/** Explicitly enroll a Talos credential and optionally persist it in an OS store. */
|
|
@@ -40,13 +40,18 @@ exports.canonicalCredentialIdentity = canonicalCredentialIdentity;
|
|
|
40
40
|
exports.credentialAccount = credentialAccount;
|
|
41
41
|
exports.createOsTalosCredentialStore = createOsTalosCredentialStore;
|
|
42
42
|
exports.talosRuntimeCredential = talosRuntimeCredential;
|
|
43
|
+
exports.createSpiffeFileRequestAuthenticator = createSpiffeFileRequestAuthenticator;
|
|
43
44
|
exports.resolveRuntimeCredential = resolveRuntimeCredential;
|
|
44
45
|
exports.enrollTalosRuntimeCredential = enrollTalosRuntimeCredential;
|
|
45
46
|
exports.enrollTalosChildRuntimeCredential = enrollTalosChildRuntimeCredential;
|
|
46
47
|
exports.authenticatedRuntimeRequest = authenticatedRuntimeRequest;
|
|
47
48
|
const node_child_process_1 = require("node:child_process");
|
|
48
49
|
const node_crypto_1 = require("node:crypto");
|
|
50
|
+
const promises_1 = require("node:fs/promises");
|
|
51
|
+
const nodeHttps = __importStar(require("node:https"));
|
|
52
|
+
const node_path_1 = require("node:path");
|
|
49
53
|
const nodeTls = __importStar(require("node:tls"));
|
|
54
|
+
const SPIFFE_CREDENTIAL_METHOD = "spiffe_x509_svid";
|
|
50
55
|
const CREDENTIAL_SERVICE = "com.ory.agent-security.runtime-credential";
|
|
51
56
|
const MACOS_CREDENTIAL_SCRIPT = String.raw `
|
|
52
57
|
ObjC.import('Foundation');
|
|
@@ -407,6 +412,7 @@ function talosRuntimeCredential(apiKey, options = {}) {
|
|
|
407
412
|
return {
|
|
408
413
|
kind: "talos",
|
|
409
414
|
...(options.subject ? { subject: options.subject } : {}),
|
|
415
|
+
apiKeyFingerprint: (0, node_crypto_1.createHash)("sha256").update(key).digest("hex"),
|
|
410
416
|
authenticate: async ({ url, init, authorizationHeader, fetch: requestFetch, usesDefaultFetch, }) => {
|
|
411
417
|
const headerName = authorizationHeader === "authorization"
|
|
412
418
|
? "authorization"
|
|
@@ -430,6 +436,205 @@ function talosRuntimeCredential(apiKey, options = {}) {
|
|
|
430
436
|
},
|
|
431
437
|
};
|
|
432
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Build an mTLS authenticator from operator-managed SPIFFE files. The files are
|
|
441
|
+
* re-read for every request so atomic SVID and trust-bundle rotations are used
|
|
442
|
+
* without restarting the process.
|
|
443
|
+
*/
|
|
444
|
+
function createSpiffeFileRequestAuthenticator(options) {
|
|
445
|
+
const read = options.readFile ?? promises_1.readFile;
|
|
446
|
+
const resolveRealpath = options.realpath ?? promises_1.realpath;
|
|
447
|
+
const requestHttps = options.request ?? nodeHttps.request;
|
|
448
|
+
const expectedServerSpiffeId = options.expectedServerSpiffeId
|
|
449
|
+
? canonicalSpiffeId(options.expectedServerSpiffeId)
|
|
450
|
+
: undefined;
|
|
451
|
+
const expectedClientSpiffeId = options.expectedClientSpiffeId
|
|
452
|
+
? canonicalSpiffeId(options.expectedClientSpiffeId, "expectedClientSpiffeId")
|
|
453
|
+
: undefined;
|
|
454
|
+
if (Boolean(expectedServerSpiffeId) === Boolean(options.verifyHostname)) {
|
|
455
|
+
throw new Error("SPIFFE file authentication requires exactly one of expectedServerSpiffeId or verifyHostname");
|
|
456
|
+
}
|
|
457
|
+
const materialPaths = async () => {
|
|
458
|
+
if (options.generationPath) {
|
|
459
|
+
const generation = await resolveRealpath(options.generationPath);
|
|
460
|
+
const files = [
|
|
461
|
+
options.certificateFile ?? "svid.pem",
|
|
462
|
+
options.privateKeyFile ?? "svid-key.pem",
|
|
463
|
+
options.trustBundleFile ?? "bundle.pem",
|
|
464
|
+
options.generationFile,
|
|
465
|
+
];
|
|
466
|
+
const paths = files.map((file) => {
|
|
467
|
+
if (file === undefined)
|
|
468
|
+
return undefined;
|
|
469
|
+
if ((0, node_path_1.isAbsolute)(file)) {
|
|
470
|
+
throw new Error("SPIFFE generation file names must be relative paths");
|
|
471
|
+
}
|
|
472
|
+
const path = (0, node_path_1.resolve)(generation, file);
|
|
473
|
+
const fromGeneration = (0, node_path_1.relative)(generation, path);
|
|
474
|
+
if (fromGeneration === ".." || fromGeneration.startsWith(`..${node_path_1.sep}`)) {
|
|
475
|
+
throw new Error("SPIFFE generation file names must remain within the generation path");
|
|
476
|
+
}
|
|
477
|
+
return path;
|
|
478
|
+
});
|
|
479
|
+
return paths;
|
|
480
|
+
}
|
|
481
|
+
if (options.generationFile) {
|
|
482
|
+
throw new Error("SPIFFE generationFile requires generationPath");
|
|
483
|
+
}
|
|
484
|
+
if (!options.certificatePath || !options.privateKeyPath || !options.trustBundlePath) {
|
|
485
|
+
throw new Error("SPIFFE file authentication requires generationPath or all three material paths");
|
|
486
|
+
}
|
|
487
|
+
return [options.certificatePath, options.privateKeyPath, options.trustBundlePath];
|
|
488
|
+
};
|
|
489
|
+
const isKeyMismatch = (error) => {
|
|
490
|
+
if (!(error instanceof Error))
|
|
491
|
+
return false;
|
|
492
|
+
const code = error.code;
|
|
493
|
+
return code === "ERR_OSSL_X509_KEY_VALUES_MISMATCH"
|
|
494
|
+
|| /key values mismatch/i.test(error.message);
|
|
495
|
+
};
|
|
496
|
+
return async ({ url, init }) => {
|
|
497
|
+
const target = new URL(url);
|
|
498
|
+
if (target.protocol !== "https:") {
|
|
499
|
+
throw new Error("SPIFFE file authentication requires an HTTPS URL");
|
|
500
|
+
}
|
|
501
|
+
if (target.username || target.password) {
|
|
502
|
+
throw new Error("SPIFFE file authentication does not accept URL credentials");
|
|
503
|
+
}
|
|
504
|
+
const normalized = new Request(target, init);
|
|
505
|
+
const body = normalized.body === null
|
|
506
|
+
? undefined
|
|
507
|
+
: Buffer.from(await normalized.arrayBuffer());
|
|
508
|
+
const send = async () => {
|
|
509
|
+
let snapshot;
|
|
510
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
511
|
+
const [certificatePath, privateKeyPath, trustBundlePath, generationFile] = await materialPaths();
|
|
512
|
+
const generationBefore = generationFile ? await read(generationFile) : undefined;
|
|
513
|
+
const [cert, key, ca] = await Promise.all([
|
|
514
|
+
read(certificatePath),
|
|
515
|
+
read(privateKeyPath),
|
|
516
|
+
read(trustBundlePath),
|
|
517
|
+
]);
|
|
518
|
+
const generationAfter = generationFile ? await read(generationFile) : undefined;
|
|
519
|
+
if (!generationBefore || (generationAfter && generationBefore.equals(generationAfter))) {
|
|
520
|
+
if (expectedClientSpiffeId) {
|
|
521
|
+
validateClientCertificate(cert, expectedClientSpiffeId);
|
|
522
|
+
}
|
|
523
|
+
snapshot = [cert, key, ca];
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (!snapshot)
|
|
528
|
+
throw new Error("SPIFFE material generation changed while loading files");
|
|
529
|
+
const [cert, key, ca] = snapshot;
|
|
530
|
+
return await new Promise((resolveResponse, reject) => {
|
|
531
|
+
const outgoing = requestHttps(target, {
|
|
532
|
+
method: normalized.method,
|
|
533
|
+
headers: Object.fromEntries(normalized.headers),
|
|
534
|
+
cert,
|
|
535
|
+
key,
|
|
536
|
+
...(expectedServerSpiffeId ? { ca } : {}),
|
|
537
|
+
...(options.privateKeyPassphrase ? { passphrase: options.privateKeyPassphrase } : {}),
|
|
538
|
+
minVersion: "TLSv1.3",
|
|
539
|
+
rejectUnauthorized: true,
|
|
540
|
+
...(expectedServerSpiffeId ? {
|
|
541
|
+
// Never reuse a connection authenticated under another expected workload ID.
|
|
542
|
+
agent: false,
|
|
543
|
+
checkServerIdentity: (_hostname, certificate) => {
|
|
544
|
+
if (!peerHasExactUriSan(certificate.subjectaltname, expectedServerSpiffeId)) {
|
|
545
|
+
return new Error(`Peer certificate does not contain expected SPIFFE URI SAN ${expectedServerSpiffeId}`);
|
|
546
|
+
}
|
|
547
|
+
return undefined;
|
|
548
|
+
},
|
|
549
|
+
} : {}),
|
|
550
|
+
signal: init.signal ?? undefined,
|
|
551
|
+
}, (incoming) => {
|
|
552
|
+
const chunks = [];
|
|
553
|
+
incoming.on("data", (chunk) => {
|
|
554
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
555
|
+
});
|
|
556
|
+
incoming.on("end", () => {
|
|
557
|
+
const headers = new Headers();
|
|
558
|
+
for (let index = 0; index < incoming.rawHeaders.length; index += 2) {
|
|
559
|
+
headers.append(incoming.rawHeaders[index], incoming.rawHeaders[index + 1]);
|
|
560
|
+
}
|
|
561
|
+
resolveResponse(new Response(Buffer.concat(chunks), {
|
|
562
|
+
status: incoming.statusCode ?? 500,
|
|
563
|
+
statusText: incoming.statusMessage,
|
|
564
|
+
headers,
|
|
565
|
+
}));
|
|
566
|
+
});
|
|
567
|
+
incoming.on("error", reject);
|
|
568
|
+
});
|
|
569
|
+
outgoing.on("error", reject);
|
|
570
|
+
if (body)
|
|
571
|
+
outgoing.write(body);
|
|
572
|
+
outgoing.end();
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
try {
|
|
576
|
+
return await send();
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (!isKeyMismatch(error))
|
|
580
|
+
throw error;
|
|
581
|
+
return await send();
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function canonicalSpiffeId(value, name = "expectedServerSpiffeId") {
|
|
586
|
+
const parsed = new URL(value);
|
|
587
|
+
if (parsed.protocol !== "spiffe:"
|
|
588
|
+
|| !parsed.hostname
|
|
589
|
+
|| parsed.username
|
|
590
|
+
|| parsed.password
|
|
591
|
+
|| parsed.port
|
|
592
|
+
|| parsed.search
|
|
593
|
+
|| parsed.hash) {
|
|
594
|
+
throw new Error(`${name} must be a valid SPIFFE ID`);
|
|
595
|
+
}
|
|
596
|
+
return value;
|
|
597
|
+
}
|
|
598
|
+
function validateClientCertificate(certificate, expectedSpiffeId) {
|
|
599
|
+
let leaf;
|
|
600
|
+
try {
|
|
601
|
+
leaf = new node_crypto_1.X509Certificate(certificate);
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
throw new Error("SPIFFE client certificate is not a valid X.509 certificate");
|
|
605
|
+
}
|
|
606
|
+
const uriSans = uriSansFromSubjectAltName(leaf.subjectAltName);
|
|
607
|
+
if (uriSans.length !== 1 || uriSans[0] !== expectedSpiffeId) {
|
|
608
|
+
throw new Error(`Client certificate must contain only the expected SPIFFE URI SAN ${expectedSpiffeId}`);
|
|
609
|
+
}
|
|
610
|
+
const now = Date.now();
|
|
611
|
+
if (now < Date.parse(leaf.validFrom) || now > Date.parse(leaf.validTo)) {
|
|
612
|
+
throw new Error("SPIFFE client certificate is not currently valid");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function uriSansFromSubjectAltName(subjectAltName) {
|
|
616
|
+
if (!subjectAltName)
|
|
617
|
+
return [];
|
|
618
|
+
const result = [];
|
|
619
|
+
for (const entry of subjectAltName.match(/(?:^|, )URI:(?:"(?:\\.|[^"\\])*"|.*?)(?=, [A-Za-z0-9.]+:|$)/g) ?? []) {
|
|
620
|
+
const encoded = entry.replace(/^, /, "").slice(4);
|
|
621
|
+
if (!encoded.startsWith('"')) {
|
|
622
|
+
result.push(encoded);
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
result.push(JSON.parse(encoded));
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
// Malformed SANs cannot satisfy an exact SPIFFE identity match.
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
function peerHasExactUriSan(subjectAltName, expected) {
|
|
635
|
+
const uriSans = uriSansFromSubjectAltName(subjectAltName);
|
|
636
|
+
return uriSans.length === 1 && uriSans[0] === expected;
|
|
637
|
+
}
|
|
433
638
|
/** Resolve only noninteractive runtime credentials. No persisted state is read. */
|
|
434
639
|
function resolveRuntimeCredential(options = {}) {
|
|
435
640
|
if (options.requestAuthenticator) {
|
|
@@ -439,11 +644,57 @@ function resolveRuntimeCredential(options = {}) {
|
|
|
439
644
|
authenticate: options.requestAuthenticator,
|
|
440
645
|
};
|
|
441
646
|
}
|
|
442
|
-
const
|
|
647
|
+
const env = options.env ?? process.env;
|
|
648
|
+
if (env.ORY_AGENT_CREDENTIAL_METHOD?.trim() === SPIFFE_CREDENTIAL_METHOD) {
|
|
649
|
+
const expectedClientSpiffeId = requiredEnv(env, "ORY_AGENT_SPIFFE_ID");
|
|
650
|
+
const generationPath = env.ORY_AGENT_SPIFFE_GENERATION_PATH?.trim();
|
|
651
|
+
const certificatePath = env.ORY_AGENT_SPIFFE_SVID_PATH?.trim();
|
|
652
|
+
const privateKeyPath = env.ORY_AGENT_SPIFFE_KEY_PATH?.trim();
|
|
653
|
+
const trustBundlePath = env.ORY_AGENT_SPIFFE_TRUST_BUNDLE_PATH?.trim();
|
|
654
|
+
const generationFile = env.ORY_SPIFFE_GENERATION_FILE?.trim();
|
|
655
|
+
const directPaths = [certificatePath, privateKeyPath, trustBundlePath];
|
|
656
|
+
if (generationPath && directPaths.some(Boolean)) {
|
|
657
|
+
throw new Error("SPIFFE configuration must use either ORY_AGENT_SPIFFE_GENERATION_PATH or direct material paths");
|
|
658
|
+
}
|
|
659
|
+
if (!generationPath && !directPaths.every(Boolean)) {
|
|
660
|
+
throw new Error("SPIFFE configuration requires ORY_AGENT_SPIFFE_GENERATION_PATH or all three direct material paths");
|
|
661
|
+
}
|
|
662
|
+
if (!generationPath && generationFile) {
|
|
663
|
+
throw new Error("ORY_SPIFFE_GENERATION_FILE requires ORY_AGENT_SPIFFE_GENERATION_PATH");
|
|
664
|
+
}
|
|
665
|
+
const expectedServerSpiffeId = env.ORY_AGENT_SPIFFE_SERVER_ID?.trim();
|
|
666
|
+
return {
|
|
667
|
+
kind: "injected",
|
|
668
|
+
...(options.subject ? { subject: options.subject } : {}),
|
|
669
|
+
authenticate: createSpiffeFileRequestAuthenticator({
|
|
670
|
+
...(generationPath ? {
|
|
671
|
+
generationPath,
|
|
672
|
+
...(generationFile
|
|
673
|
+
? { generationFile }
|
|
674
|
+
: {}),
|
|
675
|
+
} : {
|
|
676
|
+
certificatePath: certificatePath,
|
|
677
|
+
privateKeyPath: privateKeyPath,
|
|
678
|
+
trustBundlePath: trustBundlePath,
|
|
679
|
+
}),
|
|
680
|
+
expectedClientSpiffeId,
|
|
681
|
+
...(expectedServerSpiffeId
|
|
682
|
+
? { expectedServerSpiffeId }
|
|
683
|
+
: { verifyHostname: true }),
|
|
684
|
+
}),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
const apiKey = env.ORY_AGENT_API_KEY?.trim();
|
|
443
688
|
if (!apiKey)
|
|
444
689
|
return undefined;
|
|
445
690
|
return talosRuntimeCredential(apiKey, { subject: options.subject, fetch: options.fetch });
|
|
446
691
|
}
|
|
692
|
+
function requiredEnv(env, name) {
|
|
693
|
+
const value = env[name]?.trim();
|
|
694
|
+
if (!value)
|
|
695
|
+
throw new Error(`SPIFFE configuration requires ${name}`);
|
|
696
|
+
return value;
|
|
697
|
+
}
|
|
447
698
|
/** Explicitly enroll a Talos credential and optionally persist it in an OS store. */
|
|
448
699
|
async function enrollTalosRuntimeCredential(options) {
|
|
449
700
|
let response;
|
package/dist/runtime.d.ts
CHANGED
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
* wired where, so `status` can report the runtime a harness will actually
|
|
27
27
|
* load, upgrades can rewire, and unreferenced stores can be pruned.
|
|
28
28
|
*
|
|
29
|
-
* A **linked** runtime (`linkedRuntime`) points the shim straight at
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* registry involved.
|
|
29
|
+
* A **linked** runtime (`linkedRuntime`) points the shim straight at an
|
|
30
|
+
* explicit prebuilt package directory instead of a materialized store. This
|
|
31
|
+
* supports both administrator-managed runtimes and the workspace development
|
|
32
|
+
* loop (`pnpm build` + relaunch), with no registry involved.
|
|
33
33
|
*
|
|
34
34
|
* ## The shim must fail open
|
|
35
35
|
*
|
|
@@ -137,8 +137,8 @@ export declare function materializeRuntime(opts: {
|
|
|
137
137
|
installer?: NpmInstaller;
|
|
138
138
|
}): RuntimeTarget;
|
|
139
139
|
/**
|
|
140
|
-
* Use an
|
|
141
|
-
*
|
|
140
|
+
* Use an explicit prebuilt package directory as the runtime, including a
|
|
141
|
+
* managed installation or workspace build.
|
|
142
142
|
*
|
|
143
143
|
* Validates that the directory looks like a built package so a typo surfaces
|
|
144
144
|
* at install time instead of as hooks that silently never run.
|
|
@@ -206,13 +206,19 @@ export declare function writeMcpShim(opts: {
|
|
|
206
206
|
target: RuntimeTarget;
|
|
207
207
|
installCommand: string;
|
|
208
208
|
}): string | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Resolve the Node executable persisted in subprocess hook and MCP config.
|
|
211
|
+
* Managed installations can pin an administrator-approved absolute path;
|
|
212
|
+
* ordinary installations retain the portable `node` command lookup.
|
|
213
|
+
*/
|
|
214
|
+
export declare function resolvePluginNodePath(env?: NodeJS.ProcessEnv): string;
|
|
209
215
|
/**
|
|
210
216
|
* The hook command a harness config should carry: `node "<shim>"`.
|
|
211
217
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
218
|
+
* Paths are shell-quoted because managed executables and data dirs can contain
|
|
219
|
+
* spaces or shell metacharacters, and several harnesses run this as a string.
|
|
214
220
|
*/
|
|
215
|
-
export declare function hookCommand(shimPath: string): string;
|
|
221
|
+
export declare function hookCommand(shimPath: string, nodePath?: string): string;
|
|
216
222
|
/**
|
|
217
223
|
* True when a command in a harness config is one of ours.
|
|
218
224
|
*
|
|
@@ -225,7 +231,7 @@ export declare function hookCommand(shimPath: string): string;
|
|
|
225
231
|
*/
|
|
226
232
|
export declare function isOryRuntimeCommand(command: string | undefined): boolean;
|
|
227
233
|
/** The MCP server registration a harness config should carry. */
|
|
228
|
-
export declare function mcpCommand(shimPath: string): {
|
|
234
|
+
export declare function mcpCommand(shimPath: string, nodePath?: string): {
|
|
229
235
|
command: string;
|
|
230
236
|
args: string[];
|
|
231
237
|
};
|
|
@@ -296,8 +302,9 @@ export declare function pruneRuntimeStores(): string[];
|
|
|
296
302
|
* `ORY_PLUGIN_RUNTIME_DIR` → link that directory
|
|
297
303
|
* otherwise → materialize the CLI's own version
|
|
298
304
|
*
|
|
299
|
-
* The flag and the env var support explicit
|
|
300
|
-
*
|
|
305
|
+
* The flag and the env var support explicit prebuilt runtime installs,
|
|
306
|
+
* including administrator-managed package directories and workspace builds,
|
|
307
|
+
* instead of materializing from a registry.
|
|
301
308
|
*
|
|
302
309
|
* The flag deliberately requires an explicit directory. A bare "link whatever
|
|
303
310
|
* package I am running from" would resolve to npx's ephemeral cache dir under
|