@themoltnet/agent-daemon 0.44.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -23
- package/dist/cli.js +7 -61
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -102,33 +102,24 @@ any team where the agent is authorized. It fails fast if the key is rejected,
|
|
|
102
102
|
is not an agent, or a team binding mismatches. See
|
|
103
103
|
[Run the daemon with an agent key](../../docs/operate/running-agents.md#run-the-daemon-with-an-agent-key).
|
|
104
104
|
|
|
105
|
-
Daemon authentication and guest
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for local or otherwise operator-trusted tasks that genuinely need guest-shell
|
|
119
|
-
MoltNet CLI, Git signing, or GitHub authentication. OAuth2 daemons that relied
|
|
120
|
-
on the previous `guest-config` default for such tasks must now pass the flag
|
|
121
|
-
explicitly.
|
|
122
|
-
|
|
123
|
-
Trusted custom runtimes can deliver destination-bound HTTP credentials without
|
|
124
|
-
switching to `guest-config`: the guest receives an opaque placeholder and the
|
|
125
|
-
Gondolin host proxy substitutes the value only for the attested protocol,
|
|
126
|
-
hostname pattern, and port (HTTPS/443 by default). See
|
|
105
|
+
Daemon authentication and the guest boundary are two separate concerns. How the
|
|
106
|
+
daemon authenticates (an agent key, or OAuth2 resolved from
|
|
107
|
+
`.moltnet/<agent>/moltnet.json` through the host secret provider) decides how the
|
|
108
|
+
host-side SDK `Agent` is built. The guest boundary is fixed: **the guest never
|
|
109
|
+
receives MoltNet credential material.** No `.moltnet` file, gitconfig, SSH
|
|
110
|
+
signing key, GitHub App PEM, or MoltNet environment credential is injected into
|
|
111
|
+
Gondolin, and mounted `.moltnet` paths are hidden. Structured MoltNet tools
|
|
112
|
+
execute through the host-side `Agent`.
|
|
113
|
+
|
|
114
|
+
Trusted custom runtimes can deliver destination-bound HTTP credentials to the
|
|
115
|
+
guest: it receives an opaque placeholder and the Gondolin host proxy substitutes
|
|
116
|
+
the value only for the attested protocol, hostname pattern, and port (HTTPS/443
|
|
117
|
+
by default). See
|
|
127
118
|
[Host-brokered HTTP credentials](../../docs/operate/running-agents.md#host-brokered-http-credentials).
|
|
128
119
|
This does not provide diary or Git commit signing; private-key operations remain
|
|
129
120
|
host capabilities.
|
|
130
121
|
|
|
131
|
-
Host capabilities
|
|
122
|
+
Host capabilities carry in-guest signing: the stock runtime
|
|
132
123
|
declares `agent-signing`, so `git commit -S` and `moltnet entry create-signed`
|
|
133
124
|
work inside the guest while the seed stays on the host (the daemon injects a
|
|
134
125
|
signer bound to the authenticated identity and projects a non-secret gitconfig,
|
package/dist/cli.js
CHANGED
|
@@ -3387,12 +3387,6 @@ var COMMON_OPTIONAL_FLAGS = `\
|
|
|
3387
3387
|
host git config (OAuth2) or
|
|
3388
3388
|
<identityId>+<agent>[bot]@users.noreply.github.com.
|
|
3389
3389
|
Env: MOLTNET_GIT_AUTHOR.
|
|
3390
|
-
--guest-credential-mode <mode>
|
|
3391
|
-
Guest trust boundary: host-authenticated or
|
|
3392
|
-
guest-config. Defaults to host-authenticated for
|
|
3393
|
-
both agent-key and OAuth2 authentication.
|
|
3394
|
-
Explicit guest-config exposes the complete local
|
|
3395
|
-
agent credential tree to the VM.
|
|
3396
3390
|
--lease-ttl-sec <n> Sliding liveness window. Silence longer than
|
|
3397
3391
|
this ends the attempt with lease_expired.
|
|
3398
3392
|
Default: 300.
|
|
@@ -3567,21 +3561,6 @@ function isHelpFlag(args) {
|
|
|
3567
3561
|
//#endregion
|
|
3568
3562
|
//#region src/lib/agent-context.ts
|
|
3569
3563
|
/**
|
|
3570
|
-
* Guest credentials are an explicit trust decision, never an incidental
|
|
3571
|
-
* consequence of files found on disk. The guest boundary is independent of
|
|
3572
|
-
* how the daemon itself authenticates: both agent-key and OAuth2 default to
|
|
3573
|
-
* the host-authenticated boundary, where structured MoltNet operations reuse
|
|
3574
|
-
* the trusted host-side Agent and the guest receives no credential material.
|
|
3575
|
-
* OAuth2 still resolves that Agent from the local config and secret provider
|
|
3576
|
-
* on the host — reading `moltnet.json` on the host does not imply projecting
|
|
3577
|
-
* it into the guest. `guest-config` remains an explicit compatibility opt-in
|
|
3578
|
-
* for tasks that need credential-bearing guest-shell operations.
|
|
3579
|
-
*/
|
|
3580
|
-
function resolveDaemonGuestCredentialMode(requested) {
|
|
3581
|
-
if (requested !== void 0 && requested !== "guest-config" && requested !== "host-authenticated") throw new Error(`Invalid --guest-credential-mode "${requested}": expected guest-config or host-authenticated.`);
|
|
3582
|
-
return requested ?? "host-authenticated";
|
|
3583
|
-
}
|
|
3584
|
-
/**
|
|
3585
3564
|
* Report which auth mode `connect()` will use, without ever reading the secret
|
|
3586
3565
|
* value into anything logged. Agent-key mode is selected when
|
|
3587
3566
|
* `MOLTNET_AGENT_KEY` holds a non-blank value — mirroring the SDK precedence
|
|
@@ -3653,20 +3632,15 @@ async function validateStartupBinding(options) {
|
|
|
3653
3632
|
async function resolveAgentContext(agentName, options = {}) {
|
|
3654
3633
|
if (!/^[a-zA-Z0-9_-]+$/.test(agentName)) throw new Error(`Invalid agent name "${agentName}": must match /^[a-zA-Z0-9_-]+$/`);
|
|
3655
3634
|
const roots = resolveCredentialRoots(options.agentRootDir);
|
|
3656
|
-
const guestCredentialMode = resolveDaemonGuestCredentialMode(options.guestCredentialMode);
|
|
3657
3635
|
if (options.authMode === "agent-key") {
|
|
3658
|
-
const
|
|
3659
|
-
rootDir: roots[0] ?? process.cwd(),
|
|
3660
|
-
agentDir: join(roots[0] ?? process.cwd(), ".moltnet", agentName)
|
|
3661
|
-
};
|
|
3636
|
+
const rootDir = roots[0] ?? process.cwd();
|
|
3662
3637
|
return {
|
|
3663
|
-
agentDir,
|
|
3638
|
+
agentDir: join(rootDir, ".moltnet", agentName),
|
|
3664
3639
|
agentRootDir: rootDir,
|
|
3665
|
-
agent: await connect()
|
|
3666
|
-
guestCredentialMode
|
|
3640
|
+
agent: await connect()
|
|
3667
3641
|
};
|
|
3668
3642
|
}
|
|
3669
|
-
const located =
|
|
3643
|
+
const located = locateAgentConfig(roots, agentName);
|
|
3670
3644
|
if (located) {
|
|
3671
3645
|
const agent = await connect({
|
|
3672
3646
|
configDir: located.agentDir,
|
|
@@ -3675,8 +3649,7 @@ async function resolveAgentContext(agentName, options = {}) {
|
|
|
3675
3649
|
return {
|
|
3676
3650
|
agentDir: located.agentDir,
|
|
3677
3651
|
agentRootDir: located.rootDir,
|
|
3678
|
-
agent
|
|
3679
|
-
guestCredentialMode
|
|
3652
|
+
agent
|
|
3680
3653
|
};
|
|
3681
3654
|
}
|
|
3682
3655
|
const tried = roots.map((root) => join(root, ".moltnet", agentName));
|
|
@@ -3697,22 +3670,6 @@ function locateAgentConfig(roots, agentName) {
|
|
|
3697
3670
|
};
|
|
3698
3671
|
}
|
|
3699
3672
|
}
|
|
3700
|
-
function resolveCompleteGuestCredentials(roots, agentName) {
|
|
3701
|
-
const partial = [];
|
|
3702
|
-
for (const rootDir of roots) {
|
|
3703
|
-
const agentDir = join(rootDir, ".moltnet", agentName);
|
|
3704
|
-
const hasConfig = existsSync(join(agentDir, "moltnet.json"));
|
|
3705
|
-
const hasEnv = existsSync(join(agentDir, "env"));
|
|
3706
|
-
if (hasConfig && hasEnv) return {
|
|
3707
|
-
rootDir,
|
|
3708
|
-
agentDir
|
|
3709
|
-
};
|
|
3710
|
-
if (hasConfig || hasEnv) partial.push(agentDir);
|
|
3711
|
-
}
|
|
3712
|
-
if (partial.length > 0) throw new Error(`Incomplete guest credentials in ${partial.join(", ")}: moltnet.json and env must both exist for --guest-credential-mode guest-config.`);
|
|
3713
|
-
const tried = roots.map((root) => join(root, ".moltnet", agentName));
|
|
3714
|
-
throw new Error(`--guest-credential-mode guest-config requires an explicitly provisioned moltnet.json and env. Checked ${tried.join(", ")}.`);
|
|
3715
|
-
}
|
|
3716
3673
|
function resolveCredentialRoots(agentRootDir) {
|
|
3717
3674
|
const roots = agentRootDir ? [agentRootDir] : [];
|
|
3718
3675
|
try {
|
|
@@ -4995,7 +4952,6 @@ function commonOptionDefs() {
|
|
|
4995
4952
|
short: "a"
|
|
4996
4953
|
},
|
|
4997
4954
|
"agent-root": { type: "string" },
|
|
4998
|
-
"guest-credential-mode": { type: "string" },
|
|
4999
4955
|
"git-author": { type: "string" },
|
|
5000
4956
|
"lease-ttl-sec": { type: "string" },
|
|
5001
4957
|
"heartbeat-interval-ms": { type: "string" },
|
|
@@ -5712,8 +5668,7 @@ async function runPolling(opts) {
|
|
|
5712
5668
|
try {
|
|
5713
5669
|
const resolvedContext = await resolveAgentContext(baseCommon.agent, {
|
|
5714
5670
|
agentRootDir,
|
|
5715
|
-
authMode: cfg.authMode
|
|
5716
|
-
guestCredentialMode: values["guest-credential-mode"]
|
|
5671
|
+
authMode: cfg.authMode
|
|
5717
5672
|
});
|
|
5718
5673
|
gate = "authenticate_and_bind";
|
|
5719
5674
|
const whoami = await validateStartupBinding({
|
|
@@ -5782,7 +5737,6 @@ async function runPolling(opts) {
|
|
|
5782
5737
|
executables: prepared.executables
|
|
5783
5738
|
});
|
|
5784
5739
|
assertGuestEnvironmentBoundary({
|
|
5785
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
5786
5740
|
forwardEnv: profile.requiredEnv,
|
|
5787
5741
|
sandboxEnv: profile.sandboxConfig.env
|
|
5788
5742
|
});
|
|
@@ -5913,7 +5867,6 @@ async function runPolling(opts) {
|
|
|
5913
5867
|
bindingScope: startupWhoami.credentialBinding?.bindingScope ?? null,
|
|
5914
5868
|
credentialKeyId: startupWhoami.credentialBinding?.keyId ?? null,
|
|
5915
5869
|
boundTeamId: startupWhoami.credentialBinding?.bindingScope === "team" ? startupWhoami.credentialBinding.boundTeamId : null,
|
|
5916
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
5917
5870
|
taskTypes: taskTypes.length > 0 ? taskTypes : ["*"],
|
|
5918
5871
|
correlationId: values["correlation-id"] ?? null,
|
|
5919
5872
|
diaryIds: diaryIds.length > 0 ? diaryIds : ["*"],
|
|
@@ -6184,7 +6137,6 @@ async function runPolling(opts) {
|
|
|
6184
6137
|
agentIdentity,
|
|
6185
6138
|
hostCapabilitySigner,
|
|
6186
6139
|
hostCapabilityLogger: taskLogger,
|
|
6187
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6188
6140
|
agentRootDir: ctx.agentRootDir,
|
|
6189
6141
|
mountPath: sandbox.rootDir,
|
|
6190
6142
|
provider: profile.provider,
|
|
@@ -6199,7 +6151,6 @@ async function runPolling(opts) {
|
|
|
6199
6151
|
onVmDiagnostic: (diagnostic) => {
|
|
6200
6152
|
const fields = {
|
|
6201
6153
|
event: diagnostic.event,
|
|
6202
|
-
credentialMode: diagnostic.credentialMode,
|
|
6203
6154
|
...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
|
|
6204
6155
|
};
|
|
6205
6156
|
if (diagnostic.level === "warning") taskLogger.warn(fields, diagnostic.message);
|
|
@@ -6353,8 +6304,7 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6353
6304
|
try {
|
|
6354
6305
|
const resolvedContext = await resolveAgentContext(initialOpts.agent, {
|
|
6355
6306
|
agentRootDir,
|
|
6356
|
-
authMode: cfg.authMode
|
|
6357
|
-
guestCredentialMode: values["guest-credential-mode"]
|
|
6307
|
+
authMode: cfg.authMode
|
|
6358
6308
|
});
|
|
6359
6309
|
gate = "authenticate_and_bind";
|
|
6360
6310
|
const whoami = await validateStartupBinding({
|
|
@@ -6417,7 +6367,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6417
6367
|
executables: preparedRuntime.executables
|
|
6418
6368
|
});
|
|
6419
6369
|
assertGuestEnvironmentBoundary({
|
|
6420
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6421
6370
|
forwardEnv: profile.requiredEnv,
|
|
6422
6371
|
sandboxEnv: profile.sandboxConfig.env
|
|
6423
6372
|
});
|
|
@@ -6505,7 +6454,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6505
6454
|
profileId: profile.id,
|
|
6506
6455
|
profileSessionTtlSec: profile.sessionTtlSec,
|
|
6507
6456
|
profileWorkspaceTtlSec: profile.workspaceTtlSec,
|
|
6508
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6509
6457
|
piAgentDir: piAgentDir.path,
|
|
6510
6458
|
piAgentDirSource: piAgentDir.source
|
|
6511
6459
|
}, "agent-daemon.starting");
|
|
@@ -6568,7 +6516,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6568
6516
|
agentIdentity,
|
|
6569
6517
|
hostCapabilitySigner,
|
|
6570
6518
|
hostCapabilityLogger: rootLogger,
|
|
6571
|
-
guestCredentialMode: ctx.guestCredentialMode,
|
|
6572
6519
|
agentRootDir: ctx.agentRootDir,
|
|
6573
6520
|
mountPath: sandbox.rootDir,
|
|
6574
6521
|
provider: profile.provider,
|
|
@@ -6583,7 +6530,6 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
6583
6530
|
onVmDiagnostic: (diagnostic) => {
|
|
6584
6531
|
const fields = {
|
|
6585
6532
|
event: diagnostic.event,
|
|
6586
|
-
credentialMode: diagnostic.credentialMode,
|
|
6587
6533
|
...diagnostic.brokeredSecretCount !== void 0 && { brokeredSecretCount: diagnostic.brokeredSecretCount }
|
|
6588
6534
|
};
|
|
6589
6535
|
if (diagnostic.level === "warning") rootLogger.warn(fields, diagnostic.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.1",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Universal MoltNet agent daemon host with a built-in Pi/Gondolin runtime and support for trusted operator-owned runtime modules. CLI: moltnet-agent.",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"node": ">=24"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@earendil-works/gondolin": "^0.
|
|
50
|
+
"@earendil-works/gondolin": "^0.12.0",
|
|
51
51
|
"@earendil-works/pi-ai": "0.79.4",
|
|
52
52
|
"@earendil-works/pi-coding-agent": "0.79.4",
|
|
53
53
|
"@fastify/otel": "^0.18.0",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"typebox": "^1.2.8",
|
|
81
81
|
"@themoltnet/agent-runtime": "0.44.0",
|
|
82
82
|
"@themoltnet/os-keyring": "0.2.0",
|
|
83
|
-
"@themoltnet/
|
|
84
|
-
"@themoltnet/
|
|
83
|
+
"@themoltnet/pi-runtime": "0.12.1",
|
|
84
|
+
"@themoltnet/sdk": "0.137.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"tsx": "^4.7.0",
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"vite": "^8.0.0",
|
|
90
90
|
"vite-plugin-dts": "^4.5.4",
|
|
91
91
|
"vitest": "^3.0.0",
|
|
92
|
+
"@moltnet/bootstrap": "0.1.0",
|
|
92
93
|
"@moltnet/crypto-service": "0.1.0",
|
|
93
94
|
"@moltnet/models": "0.1.0",
|
|
94
95
|
"@moltnet/observability": "0.1.0",
|
|
95
|
-
"@moltnet/
|
|
96
|
-
"@moltnet/
|
|
97
|
-
"@moltnet/runtime-profiles": "0.1.0"
|
|
96
|
+
"@moltnet/runtime-profiles": "0.1.0",
|
|
97
|
+
"@moltnet/tasks": "0.1.0"
|
|
98
98
|
},
|
|
99
99
|
"nx": {
|
|
100
100
|
"projectType": "application",
|