shadok-ai 0.17.26 → 0.17.27
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 +18 -2
- package/context/agents-skill/SKILL.md +5 -2
- package/context/agents-skill/pilotctl.mjs +7 -12
- package/context/files-skill/SKILL.md +2 -3
- package/context/ledger-skill/ledger.mjs +1 -4
- package/context/pilot-prompt.md +3 -1
- package/context/reload-skill/SKILL.md +5 -2
- package/context/reload-skill/reload.mjs +2 -4
- package/context/scheduler-skill/SKILL.md +5 -2
- package/context/scheduler-skill/scripts/schedule.mjs +3 -5
- package/context/scheduler-skill/test/schedule.test.mjs +3 -1
- package/context/secrets-skill/SKILL.md +5 -2
- package/context/secrets-skill/scripts/secret.mjs +3 -6
- package/context/secrets-skill/test/secret.test.mjs +3 -1
- package/dist/accounts.d.ts +13 -0
- package/dist/accounts.js +37 -0
- package/dist/accounts.js.map +1 -1
- package/dist/agent-principal.d.ts +107 -0
- package/dist/agent-principal.js +105 -0
- package/dist/agent-principal.js.map +1 -0
- package/dist/groups.d.ts +25 -6
- package/dist/groups.js +38 -11
- package/dist/groups.js.map +1 -1
- package/dist/osusers.d.ts +2 -2
- package/dist/osusers.js +2 -2
- package/dist/secret-audit.d.ts +1 -1
- package/dist/secret-audit.js +1 -1
- package/dist/secret-audit.js.map +1 -1
- package/dist/secrets.d.ts +1 -2
- package/dist/secrets.js +4 -7
- package/dist/secrets.js.map +1 -1
- package/dist/server.d.ts +14 -0
- package/dist/server.js +170 -62
- package/dist/server.js.map +1 -1
- package/dist/session.js +8 -2
- package/dist/session.js.map +1 -1
- package/dist/tmux.js +8 -2
- package/dist/tmux.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -639,8 +639,8 @@ know before opening it up:
|
|
|
639
639
|
add, and flags it to every admin. There is no mail yet: this panel is the
|
|
640
640
|
channel, so keep an eye on its badge.
|
|
641
641
|
- While it is on, the instance password (`admin`) **cannot sign in to the
|
|
642
|
-
browser** —
|
|
643
|
-
use a named account. Agents, Telegram and peers keep working (they never use
|
|
642
|
+
browser** — no one could reset it (and agents started before it was removed
|
|
643
|
+
from their environment still hold it); use a named account. Agents, Telegram and peers keep working (they never use
|
|
644
644
|
the login), but account and two-factor management is reserved to a signed-in
|
|
645
645
|
person: an agent's key can no longer create or reset accounts.
|
|
646
646
|
- Passkeys need a **secure context**: `https://` (see "Behind TLS") or
|
|
@@ -653,6 +653,22 @@ know before opening it up:
|
|
|
653
653
|
session issued before it was switched on (they stop working). What it does
|
|
654
654
|
not: anything already running as the machine's user — same soft isolation as
|
|
655
655
|
profiles.
|
|
656
|
+
- **An agent is not the admin.** An agent authenticates to its own cockpit with
|
|
657
|
+
`SHADOK_SESSION_KEY`, and that key makes it **that agent**: a member of its own
|
|
658
|
+
channel's group, never an admin. It can drive its siblings (in multi-tenant:
|
|
659
|
+
the agents of its own group plus public ones), schedule prompts on its
|
|
660
|
+
channel, store a secret it obtained, record in the ledger, send you files,
|
|
661
|
+
reload itself — and is refused (`403`) on account, peer, Telegram, backend and
|
|
662
|
+
the other admin routes, and on every **browser-only** route (guardrails, shell
|
|
663
|
+
mode, uploads…), which now require a signed-in person's session rather than
|
|
664
|
+
an `Origin` header. Agents are no longer handed the admin's cookie
|
|
665
|
+
(`SHADOK_AUTH`) nor the instance password (`SHADOK_GUI_PASSWORD`). An agent
|
|
666
|
+
that a restricted peer spawned stays in that peer's box.
|
|
667
|
+
**Upgrading:** the first start of this version signs out whoever is signed in
|
|
668
|
+
with the instance password (once). Agents spawned before it still hold the
|
|
669
|
+
password in their environment — **change the password** (`--password` /
|
|
670
|
+
`SHADOK_GUI_PASSWORD`) and **reload those agents** (version menu → *Restart all
|
|
671
|
+
agents*) to take it away from them.
|
|
656
672
|
- Once people are named, **a prompt carries its sender**: the agent sees who
|
|
657
673
|
asked, and the other browsers show the name above the message instead of
|
|
658
674
|
"pilot (elsewhere)". The server takes that name from the session, never from
|
|
@@ -229,8 +229,11 @@ curl -sS -H "x-shadok-session-key: $SHADOK_SESSION_KEY" \
|
|
|
229
229
|
"http://127.0.0.1:$SHADOK_PORT/channels"
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
That key is your ONLY credential, and it does not expire. You are no longer
|
|
233
|
+
handed a cookie or the cockpit password, and a call made with the key is made
|
|
234
|
+
as YOU — an agent of your own group, never an admin: account, backend and
|
|
235
|
+
other cockpit-admin routes answer `403`, and so do the person-only (browser)
|
|
236
|
+
ones. That is by design; ask the human when you need one of those. A
|
|
234
237
|
`401` means that header is missing — or that you were started before this
|
|
235
238
|
existed, in which case nothing you hold can authenticate any more and only a
|
|
236
239
|
human can fix it: ask them for *Reload agent* on your ⋯ menu.
|
|
@@ -69,17 +69,12 @@ export function resolvePeer(peersOut, alias) {
|
|
|
69
69
|
if (!p || typeof p.url !== "string" || typeof p.token !== "string" || !p.url || !p.token) return null;
|
|
70
70
|
return { url: p.url.replace(/\/+$/, ""), token: p.token };
|
|
71
71
|
}
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// spawn, so it expires after a week and nothing can refresh it — an agent
|
|
79
|
-
// older than that got 401 on every call here. The key is derived from the
|
|
80
|
-
// session id, verifies for as long as the agent runs, and grants exactly what
|
|
81
|
-
// the cookie did. Both are sent: the cookie still works, and an older server
|
|
82
|
-
// that does not know the header simply ignores it.
|
|
72
|
+
// An agent authenticates with its own session key (SHADOK_SESSION_KEY) — its
|
|
73
|
+
// ONLY credential: the server no longer hands agents a cookie or the cockpit
|
|
74
|
+
// password. The key is derived from the session id, verifies for as long as the
|
|
75
|
+
// agent runs, and makes the caller THIS AGENT (its own group's agents, never an
|
|
76
|
+
// admin). A human running pilotctl by hand has no key; on a password-protected
|
|
77
|
+
// cockpit they can export SHADOK_AUTH (their own `sk_auth=…` cookie) instead.
|
|
83
78
|
export const authHeaders = () =>
|
|
84
79
|
PEER
|
|
85
80
|
? // Targeting a remote peer: the ONLY credential it accepts is the peer token.
|
|
@@ -100,7 +95,7 @@ export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
100
95
|
// `ws` already has them, so it is returned untouched.
|
|
101
96
|
//
|
|
102
97
|
// Both accept the non-standard `{ headers }` option, which is what carries the
|
|
103
|
-
//
|
|
98
|
+
// auth headers past the password gate — verified against a real upgrade
|
|
104
99
|
// handler on the built-in, not assumed.
|
|
105
100
|
function makeWS(url, opts) {
|
|
106
101
|
if (!WS)
|
|
@@ -57,9 +57,8 @@ curl -sS -X POST -H "content-type: application/json" \
|
|
|
57
57
|
"http://127.0.0.1:$SHADOK_PORT/files"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
it alone. A `401` or `403` means that header is missing — or that you were
|
|
60
|
+
That key is your only credential and it does not expire (no cookie, no
|
|
61
|
+
cockpit password is handed to an agent). A `401` or `403` means that header is missing — or that you were
|
|
63
62
|
started before this existed, in which case nothing you hold can authenticate
|
|
64
63
|
any more and only a human can fix it: ask them for *Reload agent* on your ⋯
|
|
65
64
|
menu.
|
|
@@ -30,7 +30,6 @@ import { upsertEntry, searchEntries, ageDays, resolveId, normEntity, rowKind, KI
|
|
|
30
30
|
|
|
31
31
|
const PORT = (process.env.SHADOK_PORT || "").trim();
|
|
32
32
|
const KEY = (process.env.SHADOK_SESSION_KEY || "").trim();
|
|
33
|
-
const AUTH = (process.env.SHADOK_AUTH || "").trim();
|
|
34
33
|
// On-server: a port AND a session key. The key is what proves WHICH agent calls,
|
|
35
34
|
// and it is what the server scopes the authoring group by — without it there is
|
|
36
35
|
// no identity, so we fall back to the local file.
|
|
@@ -79,9 +78,7 @@ async function api(method, p, body, { soft404 = false } = {}) {
|
|
|
79
78
|
method,
|
|
80
79
|
headers: {
|
|
81
80
|
"content-type": "application/json",
|
|
82
|
-
|
|
83
|
-
// The durable credential: the cookie in the env expires after a week,
|
|
84
|
-
// the session key does not (invariant 33).
|
|
81
|
+
// The agent's only credential; it does not expire (invariant 33).
|
|
85
82
|
...(KEY ? { "x-shadok-session-key": KEY } : {}),
|
|
86
83
|
},
|
|
87
84
|
body: body === undefined ? undefined : JSON.stringify(body),
|
package/context/pilot-prompt.md
CHANGED
|
@@ -81,7 +81,9 @@ Claude Code sessions in parallel. A human pilots you from a browser chat
|
|
|
81
81
|
- The cockpit exposes a local API at `http://127.0.0.1:$SHADOK_PORT`. If you
|
|
82
82
|
call it yourself, authenticate with `-H "x-shadok-session-key:
|
|
83
83
|
$SHADOK_SESSION_KEY"` — a `401` means that header is missing. (The shadok
|
|
84
|
-
skills already do this for you.)
|
|
84
|
+
skills already do this for you.) That key is your only credential and it
|
|
85
|
+
makes you an agent of your own group, not an admin: admin-only and
|
|
86
|
+
person-only routes answer `403` by design.
|
|
85
87
|
|
|
86
88
|
## Language — two different rules, don't mix them up
|
|
87
89
|
- **Anything you write into a repo is in English**: code comments, identifiers,
|
|
@@ -32,8 +32,11 @@ curl -sS -H "x-shadok-session-key: $SHADOK_SESSION_KEY" \
|
|
|
32
32
|
"http://127.0.0.1:$SHADOK_PORT/channels"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
That key is your ONLY credential, and it does not expire. You are no longer
|
|
36
|
+
handed a cookie or the cockpit password, and a call made with the key is made
|
|
37
|
+
as YOU — an agent of your own group, never an admin: account, backend and
|
|
38
|
+
other cockpit-admin routes answer `403`, and so do the person-only (browser)
|
|
39
|
+
ones. That is by design; ask the human when you need one of those. A
|
|
37
40
|
`401` means that header is missing — or that you were started before this
|
|
38
41
|
existed, in which case nothing you hold can authenticate any more and only a
|
|
39
42
|
human can fix it: ask them for *Reload agent* on your ⋯ menu.
|
|
@@ -6,7 +6,6 @@ import http from "node:http";
|
|
|
6
6
|
|
|
7
7
|
const key = process.env.SHADOK_SESSION_KEY;
|
|
8
8
|
const port = process.env.SHADOK_PORT || "3789";
|
|
9
|
-
const cookie = process.env.SHADOK_AUTH; // "sk_auth=…" when a GUI password is set
|
|
10
9
|
|
|
11
10
|
if (!key) {
|
|
12
11
|
console.error("SHADOK_SESSION_KEY is not set — not running under the shadok-ai cockpit?");
|
|
@@ -23,9 +22,8 @@ const req = http.request(
|
|
|
23
22
|
headers: {
|
|
24
23
|
"content-type": "application/json",
|
|
25
24
|
"content-length": Buffer.byteLength(body),
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
// which matters most here: an expired cookie left the agent unable to
|
|
25
|
+
// The agent's only credential, and it cannot age out — which matters
|
|
26
|
+
// most here: an expired cookie once left an agent unable to
|
|
29
27
|
// reach the one endpoint that would have repaired it.
|
|
30
28
|
"x-shadok-session-key": key,
|
|
31
29
|
},
|
|
@@ -137,8 +137,11 @@ curl -sS -H "x-shadok-session-key: $SHADOK_SESSION_KEY" \
|
|
|
137
137
|
"http://127.0.0.1:$SHADOK_PORT/channels"
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
That key is your ONLY credential, and it does not expire. You are no longer
|
|
141
|
+
handed a cookie or the cockpit password, and a call made with the key is made
|
|
142
|
+
as YOU — an agent of your own group, never an admin: account, backend and
|
|
143
|
+
other cockpit-admin routes answer `403`, and so do the person-only (browser)
|
|
144
|
+
ones. That is by design; ask the human when you need one of those. A
|
|
142
145
|
`401` means that header is missing — or that you were started before this
|
|
143
146
|
existed, in which case nothing you hold can authenticate any more and only a
|
|
144
147
|
human can fix it: ask them for *Reload agent* on your ⋯ menu.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// schedule — manage the CURRENT shadok-ai channel's scheduled prompts (crons)
|
|
3
3
|
// through the local server API. Reads SHADOK_SESSION_ID / SHADOK_PORT /
|
|
4
|
-
//
|
|
4
|
+
// SHADOK_SESSION_KEY from the env (injected by shadok-ai into every agent).
|
|
5
5
|
//
|
|
6
6
|
// Node, not Python: shadok-ai IS a Node program, so the runtime is guaranteed
|
|
7
7
|
// present wherever a skill runs. The Dockerfile's python3 is there to build
|
|
@@ -25,7 +25,7 @@ function env() {
|
|
|
25
25
|
const port = process.env.SHADOK_PORT;
|
|
26
26
|
const sid = process.env.SHADOK_SESSION_ID;
|
|
27
27
|
if (!port || !sid) die("Not inside a shadok-ai channel (SHADOK_PORT / SHADOK_SESSION_ID unset).");
|
|
28
|
-
return { base: `http://127.0.0.1:${port}`, sid,
|
|
28
|
+
return { base: `http://127.0.0.1:${port}`, sid, key: process.env.SHADOK_SESSION_KEY ?? "" };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
async function api(ctx, method, path, body) {
|
|
@@ -35,9 +35,7 @@ async function api(ctx, method, path, body) {
|
|
|
35
35
|
method,
|
|
36
36
|
headers: {
|
|
37
37
|
"content-type": "application/json",
|
|
38
|
-
|
|
39
|
-
// Durable half — see the note in pilotctl.mjs: the cookie is frozen
|
|
40
|
-
// into this process's env at spawn and expires after a week.
|
|
38
|
+
// The agent's ONLY credential (no cookie is handed to agents any more).
|
|
41
39
|
...(ctx.key ? { "x-shadok-session-key": ctx.key } : {}),
|
|
42
40
|
},
|
|
43
41
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
@@ -86,7 +86,9 @@ test("add posts the channel's own sessionId, the guard and the timezone", async
|
|
|
86
86
|
check: "sh guard.sh",
|
|
87
87
|
tz: "Europe/Paris",
|
|
88
88
|
});
|
|
89
|
-
|
|
89
|
+
// No cookie any more, even if one sits in the env: the session key is an
|
|
90
|
+
// agent's only credential.
|
|
91
|
+
assert.equal(srv.seen[0].cookie, undefined);
|
|
90
92
|
assert.match(res.out, /scheduled \[abcdef01\] daily 09:00 \(Europe\/Paris\) \+guard/);
|
|
91
93
|
});
|
|
92
94
|
|
|
@@ -81,8 +81,11 @@ curl -sS -H "x-shadok-session-key: $SHADOK_SESSION_KEY" \
|
|
|
81
81
|
"http://127.0.0.1:$SHADOK_PORT/channels"
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
That key is your ONLY credential, and it does not expire. You are no longer
|
|
85
|
+
handed a cookie or the cockpit password, and a call made with the key is made
|
|
86
|
+
as YOU — an agent of your own group, never an admin: account, backend and
|
|
87
|
+
other cockpit-admin routes answer `403`, and so do the person-only (browser)
|
|
88
|
+
ones. That is by design; ask the human when you need one of those. A
|
|
86
89
|
`401` means that header is missing — or that you were started before this
|
|
87
90
|
existed, in which case nothing you hold can authenticate any more and only a
|
|
88
91
|
human can fix it: ask them for *Reload agent* on your ⋯ menu.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// leaks. There is deliberately no way to read a value back out — `list` prints
|
|
8
8
|
// names only, and there is no `get`.
|
|
9
9
|
//
|
|
10
|
-
// Reads SHADOK_PORT /
|
|
10
|
+
// Reads SHADOK_PORT / SHADOK_SESSION_KEY from the env (injected into every agent).
|
|
11
11
|
//
|
|
12
12
|
// Node, not Python: shadok-ai IS a Node program, so the runtime is guaranteed
|
|
13
13
|
// present wherever a skill runs — the image's python3 exists to build native
|
|
@@ -29,7 +29,7 @@ class UsageError extends Error {}
|
|
|
29
29
|
function env() {
|
|
30
30
|
const port = process.env.SHADOK_PORT;
|
|
31
31
|
if (!port) die("Not inside a shadok-ai agent (SHADOK_PORT unset).");
|
|
32
|
-
return { base: `http://127.0.0.1:${port}`,
|
|
32
|
+
return { base: `http://127.0.0.1:${port}`, key: process.env.SHADOK_SESSION_KEY ?? "" };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
async function api(ctx, method, path, body) {
|
|
@@ -39,9 +39,7 @@ async function api(ctx, method, path, body) {
|
|
|
39
39
|
method,
|
|
40
40
|
headers: {
|
|
41
41
|
"content-type": "application/json",
|
|
42
|
-
|
|
43
|
-
// Durable half — see the note in pilotctl.mjs: the cookie is frozen
|
|
44
|
-
// into this process's env at spawn and expires after a week.
|
|
42
|
+
// The agent's ONLY credential (no cookie is handed to agents any more).
|
|
45
43
|
...(ctx.key ? { "x-shadok-session-key": ctx.key } : {}),
|
|
46
44
|
},
|
|
47
45
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
@@ -102,7 +100,6 @@ async function apiSoft(ctx, method, path, body) {
|
|
|
102
100
|
method,
|
|
103
101
|
headers: {
|
|
104
102
|
"content-type": "application/json",
|
|
105
|
-
...(ctx.auth ? { Cookie: ctx.auth } : {}),
|
|
106
103
|
...(ctx.key ? { "x-shadok-session-key": ctx.key } : {}),
|
|
107
104
|
},
|
|
108
105
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
@@ -27,7 +27,9 @@ test("set reads the value from stdin and sends it in the body", async () => {
|
|
|
27
27
|
await srv.close();
|
|
28
28
|
assert.equal(res.code, 0, res.err);
|
|
29
29
|
assert.deepEqual(srv.seen.body, { name: "A", value: "s3cr3t" });
|
|
30
|
-
|
|
30
|
+
// No cookie any more, even if one sits in the env: the session key is an
|
|
31
|
+
// agent's only credential.
|
|
32
|
+
assert.equal(srv.seen.cookie, undefined);
|
|
31
33
|
// The value must never be echoed back — the reply lands in the transcript.
|
|
32
34
|
assert.doesNotMatch(res.out, /s3cr3t/);
|
|
33
35
|
// And the user has to be told the secret is inert until attached.
|
package/dist/accounts.d.ts
CHANGED
|
@@ -79,6 +79,19 @@ export declare function userWriteVerdict(o: {
|
|
|
79
79
|
* cookie for any user. Untidy becomes impersonation the moment accounts exist.
|
|
80
80
|
*/
|
|
81
81
|
export declare function sessionSecret(): Buffer;
|
|
82
|
+
/**
|
|
83
|
+
* Bootstrap-admin cookies issued BEFORE this instant are dead.
|
|
84
|
+
*
|
|
85
|
+
* Until agents stopped being handed one, every agent carried `SHADOK_AUTH` — a
|
|
86
|
+
* bootstrap-admin cookie valid for a week, from the network too. Those cookies
|
|
87
|
+
* are indistinguishable from a person's bootstrap login, so the only way to
|
|
88
|
+
* retire them is by date: the first boot of a server that no longer issues them
|
|
89
|
+
* records "now", once, and from then on an older bootstrap cookie is refused.
|
|
90
|
+
* Cost: whoever is signed in with the instance password signs in again, once.
|
|
91
|
+
* Named accounts are untouched (their cookies were never given to agents).
|
|
92
|
+
* Best-effort: a write that fails returns 0, i.e. today's behaviour.
|
|
93
|
+
*/
|
|
94
|
+
export declare function bootstrapSessionsAfter(now: number): number;
|
|
82
95
|
/** `<user base64url>.<issuedAt>.<mfa>.<hmac>` — the name is encoded so a dot in
|
|
83
96
|
* it cannot shift the fields. The ROLE is deliberately absent: it is re-read
|
|
84
97
|
* from the account file at use time, so a demotion takes effect immediately
|
package/dist/accounts.js
CHANGED
|
@@ -59,6 +59,12 @@ export function userWriteVerdict(o) {
|
|
|
59
59
|
if (o.action === "create") {
|
|
60
60
|
if (name === BOOTSTRAP_ADMIN)
|
|
61
61
|
return { ok: false, error: `"${BOOTSTRAP_ADMIN}" is reserved for the instance password` };
|
|
62
|
+
// `agent:<sessionId>` is how an AGENT is named as a principal
|
|
63
|
+
// (src/agent-principal.ts). Authorization never trusts the name — it reads
|
|
64
|
+
// the principal's own `agent` field — but a person wearing an agent's name
|
|
65
|
+
// would be one more thing for a reader of the logs to get wrong.
|
|
66
|
+
if (name.startsWith("agent:"))
|
|
67
|
+
return { ok: false, error: `"agent:" names are reserved for agents` };
|
|
62
68
|
return o.exists ? { ok: false, error: `${name} already exists` } : { ok: true };
|
|
63
69
|
}
|
|
64
70
|
return o.exists ? { ok: true } : { ok: false, error: `no such account: ${name}` };
|
|
@@ -87,6 +93,37 @@ export function sessionSecret() {
|
|
|
87
93
|
fs.chmodSync(f, 0o600);
|
|
88
94
|
return secret;
|
|
89
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Bootstrap-admin cookies issued BEFORE this instant are dead.
|
|
98
|
+
*
|
|
99
|
+
* Until agents stopped being handed one, every agent carried `SHADOK_AUTH` — a
|
|
100
|
+
* bootstrap-admin cookie valid for a week, from the network too. Those cookies
|
|
101
|
+
* are indistinguishable from a person's bootstrap login, so the only way to
|
|
102
|
+
* retire them is by date: the first boot of a server that no longer issues them
|
|
103
|
+
* records "now", once, and from then on an older bootstrap cookie is refused.
|
|
104
|
+
* Cost: whoever is signed in with the instance password signs in again, once.
|
|
105
|
+
* Named accounts are untouched (their cookies were never given to agents).
|
|
106
|
+
* Best-effort: a write that fails returns 0, i.e. today's behaviour.
|
|
107
|
+
*/
|
|
108
|
+
export function bootstrapSessionsAfter(now) {
|
|
109
|
+
const f = path.join(os.homedir(), ".shadok-ai", "users", instanceKey() + ".bootstrap-after");
|
|
110
|
+
try {
|
|
111
|
+
const n = Number(fs.readFileSync(f, "utf8").trim());
|
|
112
|
+
if (Number.isFinite(n) && n > 0)
|
|
113
|
+
return n;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
/* first boot of this version: record it below */
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
120
|
+
fs.writeFileSync(f, String(now), { mode: 0o600 });
|
|
121
|
+
return now;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
90
127
|
/** `<user base64url>.<issuedAt>.<mfa>.<hmac>` — the name is encoded so a dot in
|
|
91
128
|
* it cannot shift the fields. The ROLE is deliberately absent: it is re-read
|
|
92
129
|
* from the account file at use time, so a demotion takes effect immediately
|
package/dist/accounts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA8CzC,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAEvC,SAAS,SAAS;IAChB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAC3D,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAe;IAC1C,MAAM,CAAC,GAAG,SAAS,EAAE,CAAC;IACtB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;oEACoE;AACpE,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACxC,OAAO,UAAU,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IACxD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC/C,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAKhC;IACC,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC;IAC9F,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC;IACxD,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,IAAI,KAAK,eAAe;YAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,eAAe,yCAAyC,EAAE,CAAC;QAC5F,8DAA8D;QAC9D,2EAA2E;QAC3E,2EAA2E;QAC3E,iEAAiE;QACjE,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,wCAAwC,EAAE,CAAC;QACrG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,IAAI,EAAE,EAAE,CAAC;AACpF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC;IACjF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,kBAAkB,CAAC,CAAC;IAC7F,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;6EAK6E;AAC7E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAgB,EAAE,MAAc,EAAE,GAAG,GAAG,KAAK;IACrF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,OAAO,GAAG,IAAI,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAa,EACb,MAAc,EACd,GAAW,EACX,QAAgB;IAEhB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC;IACtB,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAChD,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrE,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,GAAG,GAAG,QAAQ,GAAG,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,KAAK,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,MAAc,EAAE,GAAW,EAAE,QAAgB;IACtF,OAAO,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;AACrE,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAoC;IACjF,OAAO,CAAC,IAAI,CAAC,aAAa,IAAI,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC;AAC/D,CAAC;AAED;kEACkE;AAClE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,GAAG,aAAa,EAAE,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,OAA4B,EAC5B,KAAa,EACb,GAAW;IAEX,oEAAoE;IACpE,8EAA8E;IAC9E,wEAAwE;IACxE,IAAI,CAAC,OAAO;QACV,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iFAAiF,EAAE,CAAC;IACjH,IAAI,CAAC,OAAO,CAAC,MAAM;QACjB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;IACxF,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IACtF,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kDAAkD,EAAE,CAAC;IAClF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA0B,EAC1B,WAA+B,EAC/B,OAA2B,EAC3B,WAAgC;IAEhC,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,WAAW,CAAC;IACzC,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,WAAW,CAAC;IAC3C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,MAAc;IAC9D,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,GAAG,EAAE,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACvF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,MAAc;IACxD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChF,2EAA2E;IAC3E,6DAA6D;IAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,MAAc;IAC9D,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAChE,OAAO,GAAG,EAAE,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACpF,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAc;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7E,2EAA2E;IAC3E,gEAAgE;IAChE,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AGENT as a principal of its own — not the bootstrap admin.
|
|
3
|
+
*
|
|
4
|
+
* Every agent carries `SHADOK_SESSION_KEY` (`src/accounts.ts`), and the server
|
|
5
|
+
* used to resolve a valid key to `{name: BOOTSTRAP_ADMIN, role: "admin"}`. That
|
|
6
|
+
* made every agent — including a multi-tenant agent running as an unprivileged
|
|
7
|
+
* OS user precisely so it could NOT reach other groups — a full admin of the
|
|
8
|
+
* API: mint an admin invitation, rewrite the model backend, restart every
|
|
9
|
+
* agent, see and drive every group's agents. The OS-user wall was moot.
|
|
10
|
+
*
|
|
11
|
+
* A key now resolves to THE AGENT: its session id, the group its channel was
|
|
12
|
+
* born into, its profile, the person it was created for, and the restricted
|
|
13
|
+
* peer it was spawned by, if any. As an account it is a `member` (never an
|
|
14
|
+
* admin), named `agent:<sessionId>` so it can never collide with a person's
|
|
15
|
+
* name, and its group membership is exactly its channel's group — nothing a
|
|
16
|
+
* frame or a body says. Pure, so the rules are tested without a server.
|
|
17
|
+
*/
|
|
18
|
+
import type { Role } from "./accounts.js";
|
|
19
|
+
/** Who an agent is, as far as authorization goes. */
|
|
20
|
+
export interface AgentPrincipal {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
/** The group its channel was born into; null = the commons. */
|
|
23
|
+
group: string | null;
|
|
24
|
+
profile: string | null;
|
|
25
|
+
/** The person this agent ultimately works for (`Channel.createdBy`). */
|
|
26
|
+
createdBy: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* The RESTRICTED peer whose box this agent lives in, when a restricted peer
|
|
29
|
+
* spawned it (directly or through an ancestor that carried the tag). Such an
|
|
30
|
+
* agent inherits the box: it sees and drives only that peer's agents. null
|
|
31
|
+
* for an agent no restricted peer owns.
|
|
32
|
+
*/
|
|
33
|
+
peer: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** The account-shaped view of an agent: a member, never an admin. */
|
|
36
|
+
export interface AgentAccount {
|
|
37
|
+
name: string;
|
|
38
|
+
role: Role;
|
|
39
|
+
agent: AgentPrincipal;
|
|
40
|
+
}
|
|
41
|
+
/** Prefix of an agent's account name. Reserved: `userWriteVerdict` refuses a
|
|
42
|
+
* person account named with it. Nothing authorizes on the NAME anyway — an
|
|
43
|
+
* agent is recognised by the `agent` field, which only the key resolution sets. */
|
|
44
|
+
export declare const AGENT_ACCOUNT_PREFIX = "agent:";
|
|
45
|
+
export declare function agentAccountName(sessionId: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the principal behind a VERIFIED session id (the caller has already
|
|
48
|
+
* checked the key's MAC and that it came from this machine). The channel
|
|
49
|
+
* registry is the authority (invariant 1): the group, profile and creator are
|
|
50
|
+
* read from the row, never from the request.
|
|
51
|
+
*
|
|
52
|
+
* `peerScope` answers the scope of a peer by name, or null when that peer is no
|
|
53
|
+
* longer listed. A `createdByPeer` tag on the channel puts the agent in that
|
|
54
|
+
* peer's box when the peer is restricted — AND when it has been revoked: an
|
|
55
|
+
* unknown peer can no longer be proven to be a member, and the box is the
|
|
56
|
+
* fail-closed direction.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveAgentPrincipal(sessionId: string, channels: readonly {
|
|
59
|
+
sessionId: string;
|
|
60
|
+
group?: string | null;
|
|
61
|
+
profile?: string | null;
|
|
62
|
+
createdBy?: string | null;
|
|
63
|
+
createdByPeer?: string;
|
|
64
|
+
}[], peerScope: (name: string) => "member" | "restricted" | null, liveProfile?: string | null): AgentPrincipal;
|
|
65
|
+
export declare function agentAccount(p: AgentPrincipal): AgentAccount;
|
|
66
|
+
/**
|
|
67
|
+
* Credentials that must NEVER reach an agent's environment.
|
|
68
|
+
*
|
|
69
|
+
* `SHADOK_AUTH` was the bootstrap admin's cookie, injected into every agent and
|
|
70
|
+
* valid from the network for a week. `SHADOK_GUI_PASSWORD` is the instance
|
|
71
|
+
* password, which IS the bootstrap admin: the supervisor hands it to the server
|
|
72
|
+
* through the env, and every child the server spawned inherited it — node-pty
|
|
73
|
+
* copies `process.env`, a tmux pane inherits the tmux SERVER's global
|
|
74
|
+
* environment (captured whenever that server first started, possibly by an
|
|
75
|
+
* older shadok), and `setpriv` keeps the env across the uid drop. Either one
|
|
76
|
+
* let an agent `POST /login` or present a cookie as the admin, from anywhere.
|
|
77
|
+
*
|
|
78
|
+
* The agent's own credential is `SHADOK_SESSION_KEY`, which proves "I am this
|
|
79
|
+
* agent" and nothing more.
|
|
80
|
+
*/
|
|
81
|
+
export declare const AGENT_CREDENTIAL_VARS: readonly string[];
|
|
82
|
+
export declare function isAgentCredentialVar(name: string): boolean;
|
|
83
|
+
/** A copy of `env` without the credential variables. */
|
|
84
|
+
export declare function withoutAgentCredentials<T extends Record<string, string | undefined>>(env: T): T;
|
|
85
|
+
/**
|
|
86
|
+
* Is this request a PERSON in a browser on our own origin — the only caller the
|
|
87
|
+
* "browser-only" routes (guardrails, accounts, shell mode, uploads…) accept?
|
|
88
|
+
*
|
|
89
|
+
* `Origin` alone proved nothing: any process can send `Origin: http://<host>`,
|
|
90
|
+
* so an agent's `curl -H Origin:…` passed every one of those gates. Now:
|
|
91
|
+
* - a request carrying an agent's session key or a peer token is a MACHINE,
|
|
92
|
+
* whatever else it carries — a browser never sends either header;
|
|
93
|
+
* - with a password set, it must also carry a PERSON's session cookie;
|
|
94
|
+
* - and the same-origin `Origin` check stays, as the CSRF layer it always was.
|
|
95
|
+
* With no password there is no cookie to ask for (local single-user default),
|
|
96
|
+
* so the Origin check and the machine-header refusal are what remain.
|
|
97
|
+
*/
|
|
98
|
+
export declare function isBrowserPerson(o: {
|
|
99
|
+
hasAgentKey: boolean;
|
|
100
|
+
hasPeerToken: boolean;
|
|
101
|
+
passwordSet: boolean;
|
|
102
|
+
personCookie: boolean;
|
|
103
|
+
sameOrigin: boolean;
|
|
104
|
+
}): boolean;
|
|
105
|
+
/** Was a header sent at all (any non-empty value)? Presence is what makes a
|
|
106
|
+
* request a machine's — a browser never sets these, valid or not. */
|
|
107
|
+
export declare function headerPresent(v: unknown): boolean;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AGENT as a principal of its own — not the bootstrap admin.
|
|
3
|
+
*
|
|
4
|
+
* Every agent carries `SHADOK_SESSION_KEY` (`src/accounts.ts`), and the server
|
|
5
|
+
* used to resolve a valid key to `{name: BOOTSTRAP_ADMIN, role: "admin"}`. That
|
|
6
|
+
* made every agent — including a multi-tenant agent running as an unprivileged
|
|
7
|
+
* OS user precisely so it could NOT reach other groups — a full admin of the
|
|
8
|
+
* API: mint an admin invitation, rewrite the model backend, restart every
|
|
9
|
+
* agent, see and drive every group's agents. The OS-user wall was moot.
|
|
10
|
+
*
|
|
11
|
+
* A key now resolves to THE AGENT: its session id, the group its channel was
|
|
12
|
+
* born into, its profile, the person it was created for, and the restricted
|
|
13
|
+
* peer it was spawned by, if any. As an account it is a `member` (never an
|
|
14
|
+
* admin), named `agent:<sessionId>` so it can never collide with a person's
|
|
15
|
+
* name, and its group membership is exactly its channel's group — nothing a
|
|
16
|
+
* frame or a body says. Pure, so the rules are tested without a server.
|
|
17
|
+
*/
|
|
18
|
+
/** Prefix of an agent's account name. Reserved: `userWriteVerdict` refuses a
|
|
19
|
+
* person account named with it. Nothing authorizes on the NAME anyway — an
|
|
20
|
+
* agent is recognised by the `agent` field, which only the key resolution sets. */
|
|
21
|
+
export const AGENT_ACCOUNT_PREFIX = "agent:";
|
|
22
|
+
export function agentAccountName(sessionId) {
|
|
23
|
+
return `${AGENT_ACCOUNT_PREFIX}${sessionId}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the principal behind a VERIFIED session id (the caller has already
|
|
27
|
+
* checked the key's MAC and that it came from this machine). The channel
|
|
28
|
+
* registry is the authority (invariant 1): the group, profile and creator are
|
|
29
|
+
* read from the row, never from the request.
|
|
30
|
+
*
|
|
31
|
+
* `peerScope` answers the scope of a peer by name, or null when that peer is no
|
|
32
|
+
* longer listed. A `createdByPeer` tag on the channel puts the agent in that
|
|
33
|
+
* peer's box when the peer is restricted — AND when it has been revoked: an
|
|
34
|
+
* unknown peer can no longer be proven to be a member, and the box is the
|
|
35
|
+
* fail-closed direction.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveAgentPrincipal(sessionId, channels, peerScope, liveProfile) {
|
|
38
|
+
const ch = channels.find((c) => c.sessionId === sessionId);
|
|
39
|
+
const tag = ch?.createdByPeer;
|
|
40
|
+
const peer = tag && peerScope(tag) !== "member" ? tag : null;
|
|
41
|
+
return {
|
|
42
|
+
sessionId,
|
|
43
|
+
group: ch?.group ?? null,
|
|
44
|
+
profile: liveProfile ?? ch?.profile ?? null,
|
|
45
|
+
createdBy: ch?.createdBy ?? null,
|
|
46
|
+
peer,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function agentAccount(p) {
|
|
50
|
+
return { name: agentAccountName(p.sessionId), role: "member", agent: p };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Credentials that must NEVER reach an agent's environment.
|
|
54
|
+
*
|
|
55
|
+
* `SHADOK_AUTH` was the bootstrap admin's cookie, injected into every agent and
|
|
56
|
+
* valid from the network for a week. `SHADOK_GUI_PASSWORD` is the instance
|
|
57
|
+
* password, which IS the bootstrap admin: the supervisor hands it to the server
|
|
58
|
+
* through the env, and every child the server spawned inherited it — node-pty
|
|
59
|
+
* copies `process.env`, a tmux pane inherits the tmux SERVER's global
|
|
60
|
+
* environment (captured whenever that server first started, possibly by an
|
|
61
|
+
* older shadok), and `setpriv` keeps the env across the uid drop. Either one
|
|
62
|
+
* let an agent `POST /login` or present a cookie as the admin, from anywhere.
|
|
63
|
+
*
|
|
64
|
+
* The agent's own credential is `SHADOK_SESSION_KEY`, which proves "I am this
|
|
65
|
+
* agent" and nothing more.
|
|
66
|
+
*/
|
|
67
|
+
export const AGENT_CREDENTIAL_VARS = ["SHADOK_AUTH", "SHADOK_GUI_PASSWORD"];
|
|
68
|
+
export function isAgentCredentialVar(name) {
|
|
69
|
+
return AGENT_CREDENTIAL_VARS.includes(name);
|
|
70
|
+
}
|
|
71
|
+
/** A copy of `env` without the credential variables. */
|
|
72
|
+
export function withoutAgentCredentials(env) {
|
|
73
|
+
const out = { ...env };
|
|
74
|
+
for (const k of AGENT_CREDENTIAL_VARS)
|
|
75
|
+
delete out[k];
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Is this request a PERSON in a browser on our own origin — the only caller the
|
|
80
|
+
* "browser-only" routes (guardrails, accounts, shell mode, uploads…) accept?
|
|
81
|
+
*
|
|
82
|
+
* `Origin` alone proved nothing: any process can send `Origin: http://<host>`,
|
|
83
|
+
* so an agent's `curl -H Origin:…` passed every one of those gates. Now:
|
|
84
|
+
* - a request carrying an agent's session key or a peer token is a MACHINE,
|
|
85
|
+
* whatever else it carries — a browser never sends either header;
|
|
86
|
+
* - with a password set, it must also carry a PERSON's session cookie;
|
|
87
|
+
* - and the same-origin `Origin` check stays, as the CSRF layer it always was.
|
|
88
|
+
* With no password there is no cookie to ask for (local single-user default),
|
|
89
|
+
* so the Origin check and the machine-header refusal are what remain.
|
|
90
|
+
*/
|
|
91
|
+
export function isBrowserPerson(o) {
|
|
92
|
+
if (o.hasAgentKey || o.hasPeerToken)
|
|
93
|
+
return false;
|
|
94
|
+
if (o.passwordSet && !o.personCookie)
|
|
95
|
+
return false;
|
|
96
|
+
return o.sameOrigin;
|
|
97
|
+
}
|
|
98
|
+
/** Was a header sent at all (any non-empty value)? Presence is what makes a
|
|
99
|
+
* request a machine's — a browser never sets these, valid or not. */
|
|
100
|
+
export function headerPresent(v) {
|
|
101
|
+
if (Array.isArray(v))
|
|
102
|
+
return v.some((x) => typeof x === "string" && x.length > 0);
|
|
103
|
+
return typeof v === "string" && v.length > 0;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=agent-principal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-principal.js","sourceRoot":"","sources":["../src/agent-principal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA4BH;;oFAEoF;AACpF,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,GAAG,oBAAoB,GAAG,SAAS,EAAE,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,SAAiB,EACjB,QAMG,EACH,SAA2D,EAC3D,WAA2B;IAE3B,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,EAAE,EAAE,aAAa,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,OAAO;QACL,SAAS;QACT,KAAK,EAAE,EAAE,EAAE,KAAK,IAAI,IAAI;QACxB,OAAO,EAAE,WAAW,IAAI,EAAE,EAAE,OAAO,IAAI,IAAI;QAC3C,SAAS,EAAE,EAAE,EAAE,SAAS,IAAI,IAAI;QAChC,IAAI;KACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAiB;IAC5C,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC;AAE/F,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,uBAAuB,CAA+C,GAAM;IAC1F,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,IAAI,qBAAqB;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,CAM/B;IACC,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IACnD,OAAO,CAAC,CAAC,UAAU,CAAC;AACtB,CAAC;AAED;sEACsE;AACtE,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClF,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/C,CAAC"}
|