asylumwarden 0.0.0 → 0.1.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 +10 -5
- package/adapters/opencode/index.js +282 -0
- package/bin/asylumwarden.mjs +5 -0
- package/package.json +14 -1
- package/schemas/config.schema.json +93 -0
- package/schemas/ledger.schema.json +21 -0
- package/schemas/manifest.schema.json +49 -0
- package/schemas/receipt.schema.json +102 -0
- package/schemas/registry.schema.json +43 -0
- package/src/cli/main.mjs +342 -0
- package/src/config/bootstrap.mjs +36 -0
- package/src/config/load.mjs +531 -0
- package/src/config/schemas.mjs +58 -0
- package/src/context.mjs +50 -0
- package/src/index.mjs +12 -0
- package/src/kernel/canon.mjs +59 -0
- package/src/kernel/errors.mjs +80 -0
- package/src/kernel/fsutil.mjs +147 -0
- package/src/kernel/glob.mjs +70 -0
- package/src/kernel/lock.mjs +104 -0
- package/src/kernel/schemalite.mjs +153 -0
- package/src/kernel/state.mjs +56 -0
- package/src/lease/lease.mjs +551 -0
- package/src/ledger/anchor.mjs +395 -0
- package/src/ledger/chain.mjs +468 -0
- package/src/manifest/manifest.mjs +199 -0
- package/src/manifest/plan.mjs +142 -0
- package/src/receipt/receipt.mjs +555 -0
- package/src/views/views.mjs +148 -0
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# AsylumWarden
|
|
2
2
|
|
|
3
|
-
**Status:
|
|
4
|
-
is a name reservation; the
|
|
3
|
+
**Status: core + CLI implemented (milestones M1–M2), adapter layer in
|
|
4
|
+
progress. The npm package `asylumwarden@0.0.0` is a name reservation; the
|
|
5
|
+
first functional release will be `0.1.0`.**
|
|
5
6
|
|
|
6
7
|
AsylumWarden is a local, file-first control plane for multi-seat AI work. It
|
|
7
8
|
keeps the plan, leases, receipts, and ledger on disk. It does not run a daemon,
|
|
8
9
|
require a database, or replace the host's sandbox.
|
|
9
10
|
|
|
10
11
|
- GitHub: <https://github.com/TachikomaGundam/AsylumWarden>
|
|
11
|
-
- npm: `asylumwarden` (0.0.0 name reservation;
|
|
12
|
+
- npm: `asylumwarden` (0.0.0 name reservation; functional release `0.1.0`
|
|
13
|
+
follows the release gate in [`docs/RELEASING.md`](docs/RELEASING.md))
|
|
12
14
|
- License: MIT
|
|
13
15
|
|
|
14
16
|
## What it is
|
|
@@ -30,8 +32,11 @@ require a database, or replace the host's sandbox.
|
|
|
30
32
|
## Design
|
|
31
33
|
|
|
32
34
|
The approved architecture is in
|
|
33
|
-
[`docs/
|
|
34
|
-
|
|
35
|
+
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). Wire formats
|
|
36
|
+
and error codes have a single source: [`docs/CONTRACT.md`](docs/CONTRACT.md)
|
|
37
|
+
with machine-readable schemas in `schemas/`. Security limits are stated
|
|
38
|
+
plainly in [`docs/SECURITY.md`](docs/SECURITY.md). The PCB pilot boundary is
|
|
39
|
+
in [`docs/PILOT-PCB.md`](docs/PILOT-PCB.md). Release rules are in
|
|
35
40
|
[`docs/RELEASING.md`](docs/RELEASING.md).
|
|
36
41
|
|
|
37
42
|
## Boundary with Abathur
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// THIN ADAPTER (M3). No business rules here; all validation is core's (fail-closed).
|
|
2
|
+
// Every warden_* handler does exactly one thing: map tool args -> CLI argv, run the
|
|
3
|
+
// core in-process via runCli(), return { output, exit }. No retries, no state
|
|
4
|
+
// access, no defaults, no interpretation of the envelope.
|
|
5
|
+
//
|
|
6
|
+
// PCB pilot boundary (docs/PILOT-PCB.md): this adapter must NEVER be installed into
|
|
7
|
+
// PCB sessions. The PCB pilot is CLI-only and shadow-only; the plugin path is out
|
|
8
|
+
// of bounds for that line.
|
|
9
|
+
//
|
|
10
|
+
// Plugin shape: verified against a real installed plugin on this machine,
|
|
11
|
+
// opencode-wiki-historian@0.6.3 (dist/index.js + dist/tools.js), 2026-11-05:
|
|
12
|
+
//
|
|
13
|
+
// // dist/index.js (historian):
|
|
14
|
+
// async function server(input, options) { ... return { tool: buildTools(opts) }; }
|
|
15
|
+
// const plugin = { id: 'opencode-historian', server };
|
|
16
|
+
// export default plugin;
|
|
17
|
+
// // dist/tools/read.js (historian):
|
|
18
|
+
// import { tool } from '@opencode-ai/plugin';
|
|
19
|
+
// const s = tool.schema; // zod
|
|
20
|
+
// tool({ description, args: { key: s.string() }, execute: async (raw) => string })
|
|
21
|
+
//
|
|
22
|
+
// The real plugin builds each tool with the SDK's `tool()` helper and zod schemas.
|
|
23
|
+
// This adapter must not import any host SDK (AGENTS.md: adapters never create a
|
|
24
|
+
// path back into core; the zero-dep promise; task constraint), so tool definitions
|
|
25
|
+
// are the same plain objects the helper returns — { description, args, execute } —
|
|
26
|
+
// with `args` declared as JSON Schema instead of zod. If the host-side loader ever
|
|
27
|
+
// needs zod args, that wrapping belongs at install time, not in core and not here.
|
|
28
|
+
// Everything host-facing is duck-typed: no @opencode-ai/* import anywhere, static
|
|
29
|
+
// or dynamic. If the host shape is missing/unexpected we register nothing and log
|
|
30
|
+
// once — never throw at load.
|
|
31
|
+
//
|
|
32
|
+
// Core is imported through the public library entry only (src/index.mjs exports
|
|
33
|
+
// runCli); zero subprocess. Import direction: adapters -> core, never the reverse
|
|
34
|
+
// (enforced by tools/import-gate.mjs).
|
|
35
|
+
|
|
36
|
+
import { runCli } from "../../src/index.mjs";
|
|
37
|
+
|
|
38
|
+
// CLI surface per docs/CONTRACT.md §8. `command` = fixed leading argv tokens;
|
|
39
|
+
// positional/value/bool entries map 1:1 onto CLI flags (camelCase tool param ->
|
|
40
|
+
// kebab-case flag, e.g. stateHome -> --state-home). Required-ness is NOT checked
|
|
41
|
+
// here — core fails closed (USAGE, exit 1) on a missing --actor/--epoch/etc.
|
|
42
|
+
const TOOL_SPECS = [
|
|
43
|
+
{
|
|
44
|
+
tool: "warden_init",
|
|
45
|
+
command: ["init"],
|
|
46
|
+
description: "Initialize the state home (genesis ledger entry + anchor push). " +
|
|
47
|
+
"With bootstrapRegistry=true, create an absent registry file instead (needs registry, self, root).",
|
|
48
|
+
positional: [],
|
|
49
|
+
valueFlags: [
|
|
50
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
51
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override (test/shadow)" },
|
|
52
|
+
{ key: "registry", flag: "registry", desc: "Registry path (with bootstrapRegistry)" },
|
|
53
|
+
{ key: "self", flag: "self", desc: "Own line name (with bootstrapRegistry)" },
|
|
54
|
+
{ key: "root", flag: "root", desc: "Own writable root (with bootstrapRegistry)" },
|
|
55
|
+
],
|
|
56
|
+
boolFlags: [
|
|
57
|
+
{ key: "force", flag: "force", desc: "Overwrite an existing registry file" },
|
|
58
|
+
{ key: "bootstrapRegistry", flag: "bootstrap-registry", desc: "Bootstrap the registry file" },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
tool: "warden_plan",
|
|
63
|
+
command: ["plan"],
|
|
64
|
+
description: "Validate a wave manifest and materialize the wave.",
|
|
65
|
+
positional: [{ key: "manifest", desc: "Path to wave.json" }],
|
|
66
|
+
valueFlags: [
|
|
67
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
68
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
69
|
+
],
|
|
70
|
+
boolFlags: [{ key: "force", flag: "force", desc: "Force re-plan" }],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
tool: "warden_ready",
|
|
74
|
+
command: ["ready"],
|
|
75
|
+
description: "List claimable cells of a wave (pending, dependencies done).",
|
|
76
|
+
positional: [{ key: "wave", desc: "Wave id" }],
|
|
77
|
+
valueFlags: [
|
|
78
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
79
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
80
|
+
],
|
|
81
|
+
boolFlags: [],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
tool: "warden_claim",
|
|
85
|
+
command: ["claim"],
|
|
86
|
+
description: "Acquire the lease on a cell (owner, epoch, TTL).",
|
|
87
|
+
positional: [
|
|
88
|
+
{ key: "wave", desc: "Wave id" },
|
|
89
|
+
{ key: "cell", desc: "Cell id" },
|
|
90
|
+
],
|
|
91
|
+
valueFlags: [
|
|
92
|
+
{ key: "actor", flag: "actor", desc: "Actor name (required by core)" },
|
|
93
|
+
{ key: "ttl", flag: "ttl", desc: "Lease TTL in seconds", type: "number" },
|
|
94
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
95
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
96
|
+
],
|
|
97
|
+
boolFlags: [{ key: "steal", flag: "steal", desc: "Steal a live lease (audited)" }],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
tool: "warden_renew",
|
|
101
|
+
command: ["renew"],
|
|
102
|
+
description: "Heartbeat: extend the lease of the current epoch.",
|
|
103
|
+
positional: [
|
|
104
|
+
{ key: "wave", desc: "Wave id" },
|
|
105
|
+
{ key: "cell", desc: "Cell id" },
|
|
106
|
+
],
|
|
107
|
+
valueFlags: [
|
|
108
|
+
{ key: "actor", flag: "actor", desc: "Actor name (required by core)" },
|
|
109
|
+
{ key: "epoch", flag: "epoch", desc: "Lease epoch (required by core)", type: "number" },
|
|
110
|
+
{ key: "ttl", flag: "ttl", desc: "New TTL in seconds", type: "number" },
|
|
111
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
112
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
113
|
+
],
|
|
114
|
+
boolFlags: [],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
tool: "warden_release",
|
|
118
|
+
command: ["release"],
|
|
119
|
+
description: "Voluntarily release a held lease.",
|
|
120
|
+
positional: [
|
|
121
|
+
{ key: "wave", desc: "Wave id" },
|
|
122
|
+
{ key: "cell", desc: "Cell id" },
|
|
123
|
+
],
|
|
124
|
+
valueFlags: [
|
|
125
|
+
{ key: "actor", flag: "actor", desc: "Actor name (required by core)" },
|
|
126
|
+
{ key: "epoch", flag: "epoch", desc: "Lease epoch (required by core)", type: "number" },
|
|
127
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
128
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
129
|
+
],
|
|
130
|
+
boolFlags: [],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
tool: "warden_requeue",
|
|
134
|
+
command: ["requeue"],
|
|
135
|
+
description: "Move a failed cell back to pending.",
|
|
136
|
+
positional: [
|
|
137
|
+
{ key: "wave", desc: "Wave id" },
|
|
138
|
+
{ key: "cell", desc: "Cell id" },
|
|
139
|
+
],
|
|
140
|
+
valueFlags: [
|
|
141
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
142
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
143
|
+
],
|
|
144
|
+
boolFlags: [],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
tool: "warden_done",
|
|
148
|
+
command: ["done"],
|
|
149
|
+
description: "Finish a cell as done with a receipt (core validates the receipt).",
|
|
150
|
+
positional: [
|
|
151
|
+
{ key: "wave", desc: "Wave id" },
|
|
152
|
+
{ key: "cell", desc: "Cell id" },
|
|
153
|
+
],
|
|
154
|
+
valueFlags: [
|
|
155
|
+
{ key: "actor", flag: "actor", desc: "Actor name (required by core)" },
|
|
156
|
+
{ key: "epoch", flag: "epoch", desc: "Lease epoch (required by core)", type: "number" },
|
|
157
|
+
{ key: "receipt", flag: "receipt", desc: "Receipt file path (required by core)" },
|
|
158
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
159
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
160
|
+
],
|
|
161
|
+
boolFlags: [],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
tool: "warden_fail",
|
|
165
|
+
command: ["fail"],
|
|
166
|
+
description: "Finish a cell as failed with a reason.",
|
|
167
|
+
positional: [
|
|
168
|
+
{ key: "wave", desc: "Wave id" },
|
|
169
|
+
{ key: "cell", desc: "Cell id" },
|
|
170
|
+
],
|
|
171
|
+
valueFlags: [
|
|
172
|
+
{ key: "actor", flag: "actor", desc: "Actor name (required by core)" },
|
|
173
|
+
{ key: "epoch", flag: "epoch", desc: "Lease epoch (required by core)", type: "number" },
|
|
174
|
+
{ key: "reason", flag: "reason", desc: "Failure reason (required by core)" },
|
|
175
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
176
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
177
|
+
],
|
|
178
|
+
boolFlags: [],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
tool: "warden_status",
|
|
182
|
+
command: ["status"],
|
|
183
|
+
description: "Status report for one wave or all waves.",
|
|
184
|
+
positional: [{ key: "wave", desc: "Wave id (omit for all waves)" }],
|
|
185
|
+
valueFlags: [
|
|
186
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
187
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
188
|
+
],
|
|
189
|
+
boolFlags: [],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
tool: "warden_verify",
|
|
193
|
+
command: ["verify"],
|
|
194
|
+
description: "Verify the ledger hash chain + anchor + receipt spot-check.",
|
|
195
|
+
positional: [],
|
|
196
|
+
valueFlags: [
|
|
197
|
+
{ key: "wave", flag: "wave", desc: "Restrict verification to one wave" },
|
|
198
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
199
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
200
|
+
],
|
|
201
|
+
boolFlags: [],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
tool: "warden_registry_check",
|
|
205
|
+
command: ["registry", "check"],
|
|
206
|
+
description: "Validate the registry, print its digest and the line table.",
|
|
207
|
+
positional: [],
|
|
208
|
+
valueFlags: [
|
|
209
|
+
{ key: "config", flag: "config", desc: "Path to asylumwarden.json" },
|
|
210
|
+
{ key: "stateHome", flag: "state-home", desc: "State home override" },
|
|
211
|
+
],
|
|
212
|
+
boolFlags: [],
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
// Pure arg -> argv mapping. Undefined/null params are omitted so core sees
|
|
217
|
+
// exactly what the caller passed (core decides required-ness, fail-closed).
|
|
218
|
+
export function buildArgv(spec, args) {
|
|
219
|
+
const argv = [...spec.command];
|
|
220
|
+
for (const p of spec.positional) {
|
|
221
|
+
if (args[p.key] !== undefined && args[p.key] !== null) argv.push(String(args[p.key]));
|
|
222
|
+
}
|
|
223
|
+
for (const f of spec.valueFlags) {
|
|
224
|
+
if (args[f.key] !== undefined && args[f.key] !== null) argv.push(`--${f.flag}`, String(args[f.key]));
|
|
225
|
+
}
|
|
226
|
+
for (const b of spec.boolFlags) {
|
|
227
|
+
if (args[b.key] === true) argv.push(`--${b.flag}`);
|
|
228
|
+
}
|
|
229
|
+
return argv;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function jsonSchemaArgs(spec) {
|
|
233
|
+
const args = {};
|
|
234
|
+
for (const p of spec.positional) args[p.key] = { type: "string", description: p.desc };
|
|
235
|
+
for (const f of spec.valueFlags) args[f.key] = { type: f.type ?? "string", description: f.desc };
|
|
236
|
+
for (const b of spec.boolFlags) args[b.key] = { type: "boolean", description: b.desc };
|
|
237
|
+
return args;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function buildTools() {
|
|
241
|
+
const tools = {};
|
|
242
|
+
for (const spec of TOOL_SPECS) {
|
|
243
|
+
tools[spec.tool] = {
|
|
244
|
+
description: spec.description,
|
|
245
|
+
args: jsonSchemaArgs(spec),
|
|
246
|
+
execute: async (rawArgs) => {
|
|
247
|
+
const argv = buildArgv(spec, rawArgs ?? {});
|
|
248
|
+
let output = "";
|
|
249
|
+
const exit = await runCli(argv, { write: (chunk) => { output += chunk; } });
|
|
250
|
+
return { output, exit };
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return tools;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let logged = false;
|
|
258
|
+
function logOnce(reason) {
|
|
259
|
+
if (logged) return;
|
|
260
|
+
logged = true;
|
|
261
|
+
console.error(`[asylumwarden-adapter] ${reason} — no warden_* tools registered (the CLI still works).`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Same hooks contract the historian plugin ships: server(input, options) resolves
|
|
265
|
+
// hooks whose `tool` field maps tool name -> definition. Degrades to a no-op
|
|
266
|
+
// (empty hooks, one console.error) when the host passes a missing/unexpected
|
|
267
|
+
// shape — registration must never crash host startup.
|
|
268
|
+
async function server(input, _options) {
|
|
269
|
+
try {
|
|
270
|
+
if (input === null || typeof input !== "object") {
|
|
271
|
+
logOnce(`unexpected host API shape: server(input) received ${input === null ? "null" : typeof input}`);
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
274
|
+
return { tool: buildTools() };
|
|
275
|
+
} catch (err) {
|
|
276
|
+
logOnce(`tool registration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
277
|
+
return {};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const plugin = { id: "asylumwarden", server };
|
|
282
|
+
export default plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asylumwarden",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local, file-first control plane for multi-seat AI work: registry, DAG, leases, receipts, ledger.",
|
|
6
6
|
"type": "module",
|
|
@@ -8,10 +8,23 @@
|
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=22.22.1"
|
|
10
10
|
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"asylumwarden": "bin/asylumwarden.mjs"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node tools/workfile-gate.mjs && node --test 'test/**/*.test.mjs'",
|
|
16
|
+
"gate:imports": "node tools/import-gate.mjs",
|
|
17
|
+
"gate:workfiles": "node tools/workfile-gate.mjs"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.mjs",
|
|
21
|
+
"./server": "./adapters/opencode/index.js"
|
|
22
|
+
},
|
|
11
23
|
"files": [
|
|
12
24
|
"src",
|
|
13
25
|
"schemas",
|
|
14
26
|
"bin",
|
|
27
|
+
"adapters",
|
|
15
28
|
"README.md",
|
|
16
29
|
"LICENSE"
|
|
17
30
|
],
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "asylumwarden/config.schema.json",
|
|
4
|
+
"title": "AsylumWarden project config (asylumwarden.json)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"allOf": [
|
|
7
|
+
{
|
|
8
|
+
"anyOf": [
|
|
9
|
+
{
|
|
10
|
+
"description": "normal mode: shadow keys forbidden",
|
|
11
|
+
"properties": {
|
|
12
|
+
"version": { "const": 1 },
|
|
13
|
+
"registry": { "type": "string", "pattern": "^~?/" },
|
|
14
|
+
"registry_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
15
|
+
"self": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
16
|
+
"state_home": { "type": "string", "pattern": "^~?/" },
|
|
17
|
+
"mode": { "const": "normal" },
|
|
18
|
+
"kinds": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"additionalProperties": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": { "description": { "type": "string", "minLength": 1, "maxLength": 4096 } },
|
|
23
|
+
"required": ["description"],
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"anchor": {
|
|
28
|
+
"anyOf": [
|
|
29
|
+
{ "type": "null" },
|
|
30
|
+
{
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"type": { "enum": ["command", "remote", "human-checkpoint", "file"] },
|
|
34
|
+
"target": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
35
|
+
"signer": { "type": "string", "minLength": 1, "maxLength": 256 }
|
|
36
|
+
},
|
|
37
|
+
"required": ["type", "target"],
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": ["version", "registry", "registry_sha256", "self", "state_home", "mode"],
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"description": "shadow mode: shadow_of and shadow_home required",
|
|
48
|
+
"properties": {
|
|
49
|
+
"version": { "const": 1 },
|
|
50
|
+
"registry": { "type": "string", "pattern": "^~?/" },
|
|
51
|
+
"registry_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
52
|
+
"self": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
53
|
+
"state_home": { "type": "string", "pattern": "^~?/" },
|
|
54
|
+
"mode": { "const": "shadow" },
|
|
55
|
+
"shadow_of": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
56
|
+
"shadow_home": { "type": "string", "pattern": "^/" },
|
|
57
|
+
"protect": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"maxItems": 64,
|
|
60
|
+
"items": { "type": "string", "pattern": "^~?/" }
|
|
61
|
+
},
|
|
62
|
+
"kinds": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": { "description": { "type": "string", "minLength": 1, "maxLength": 4096 } },
|
|
67
|
+
"required": ["description"],
|
|
68
|
+
"additionalProperties": false
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"anchor": {
|
|
72
|
+
"anyOf": [
|
|
73
|
+
{ "type": "null" },
|
|
74
|
+
{
|
|
75
|
+
"type": "object",
|
|
76
|
+
"properties": {
|
|
77
|
+
"type": { "enum": ["command", "remote", "human-checkpoint", "file"] },
|
|
78
|
+
"target": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
79
|
+
"signer": { "type": "string", "minLength": 1, "maxLength": 256 }
|
|
80
|
+
},
|
|
81
|
+
"required": ["type", "target"],
|
|
82
|
+
"additionalProperties": false
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"required": ["version", "registry", "registry_sha256", "self", "state_home", "mode", "shadow_of", "shadow_home", "protect"],
|
|
88
|
+
"additionalProperties": false
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "asylumwarden/ledger.schema.json",
|
|
4
|
+
"title": "AsylumWarden ledger entry (one per JSONL line)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"seq": { "type": "integer", "minimum": 1 },
|
|
8
|
+
"ts": { "type": "string", "format": "date-time" },
|
|
9
|
+
"type": {
|
|
10
|
+
"enum": ["genesis", "plan", "claim", "renew", "release", "requeue", "steal", "done", "fail", "anchor"]
|
|
11
|
+
},
|
|
12
|
+
"wave": { "type": ["string", "null"], "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
13
|
+
"cell": { "type": ["string", "null"], "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
14
|
+
"actor": { "type": ["string", "null"], "minLength": 1, "maxLength": 256 },
|
|
15
|
+
"payload": { "type": "object" },
|
|
16
|
+
"prev": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
17
|
+
"hash": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
|
18
|
+
},
|
|
19
|
+
"required": ["seq", "ts", "type", "wave", "cell", "actor", "payload", "prev", "hash"],
|
|
20
|
+
"additionalProperties": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "asylumwarden/manifest.schema.json",
|
|
4
|
+
"title": "AsylumWarden wave manifest (wave.json)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"version": { "const": 1 },
|
|
8
|
+
"wave": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
9
|
+
"cells": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"minItems": 1,
|
|
12
|
+
"maxItems": 1024,
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
17
|
+
"kind": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$" },
|
|
18
|
+
"line": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
19
|
+
"cwd": { "type": "string", "pattern": "^/" },
|
|
20
|
+
"allowed_roots": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"minItems": 1,
|
|
23
|
+
"maxItems": 64,
|
|
24
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
25
|
+
},
|
|
26
|
+
"read_roots": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"maxItems": 64,
|
|
29
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
30
|
+
},
|
|
31
|
+
"deps": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"maxItems": 1024,
|
|
34
|
+
"items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" }
|
|
35
|
+
},
|
|
36
|
+
"role": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
37
|
+
"role_file": { "type": "string", "pattern": "^/" },
|
|
38
|
+
"session_started_at": { "type": "string", "format": "date-time" },
|
|
39
|
+
"brief": { "type": "string", "maxLength": 4096 },
|
|
40
|
+
"acceptance": { "type": "string", "maxLength": 4096 }
|
|
41
|
+
},
|
|
42
|
+
"required": ["id", "kind", "line", "cwd", "allowed_roots", "deps"],
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": ["version", "wave", "cells"],
|
|
48
|
+
"additionalProperties": false
|
|
49
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "asylumwarden/receipt.schema.json",
|
|
4
|
+
"title": "AsylumWarden receipt",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"allOf": [
|
|
7
|
+
{
|
|
8
|
+
"anyOf": [
|
|
9
|
+
{
|
|
10
|
+
"description": "normal receipt: shadow keys forbidden",
|
|
11
|
+
"properties": {
|
|
12
|
+
"version": { "const": 1 },
|
|
13
|
+
"cell": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
14
|
+
"actor": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
15
|
+
"epoch": { "type": "integer", "minimum": 1 },
|
|
16
|
+
"status": { "enum": ["done", "failed"] },
|
|
17
|
+
"summary": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
18
|
+
"commands": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"maxItems": 256,
|
|
21
|
+
"items": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"properties": {
|
|
24
|
+
"cmd": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
25
|
+
"exit": { "type": "integer", "minimum": 0 }
|
|
26
|
+
},
|
|
27
|
+
"required": ["cmd", "exit"],
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"artifacts": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"maxItems": 1024,
|
|
34
|
+
"items": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {
|
|
37
|
+
"path": { "type": "string", "pattern": "^/" },
|
|
38
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
|
39
|
+
},
|
|
40
|
+
"required": ["path", "sha256"],
|
|
41
|
+
"additionalProperties": false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"reads": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"maxItems": 4096,
|
|
47
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"required": ["version", "cell", "actor", "epoch", "status", "summary"],
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"description": "shadow receipt: mode and shadow_of required; wave id carries the shadow- prefix",
|
|
55
|
+
"properties": {
|
|
56
|
+
"version": { "const": 1 },
|
|
57
|
+
"cell": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
58
|
+
"actor": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
59
|
+
"epoch": { "type": "integer", "minimum": 1 },
|
|
60
|
+
"status": { "enum": ["done", "failed"] },
|
|
61
|
+
"summary": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
62
|
+
"mode": { "const": "shadow" },
|
|
63
|
+
"shadow_of": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
64
|
+
"commands": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"maxItems": 256,
|
|
67
|
+
"items": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"cmd": { "type": "string", "minLength": 1, "maxLength": 4096 },
|
|
71
|
+
"exit": { "type": "integer", "minimum": 0 }
|
|
72
|
+
},
|
|
73
|
+
"required": ["cmd", "exit"],
|
|
74
|
+
"additionalProperties": false
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"artifacts": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"maxItems": 1024,
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"properties": {
|
|
83
|
+
"path": { "type": "string", "pattern": "^/" },
|
|
84
|
+
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
|
85
|
+
},
|
|
86
|
+
"required": ["path", "sha256"],
|
|
87
|
+
"additionalProperties": false
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"reads": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"maxItems": 4096,
|
|
93
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"required": ["version", "cell", "actor", "epoch", "status", "summary", "mode", "shadow_of"],
|
|
97
|
+
"additionalProperties": false
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "asylumwarden/registry.schema.json",
|
|
4
|
+
"title": "AsylumWarden machine registry (registry.json / shadow-registry.json)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"version": { "const": 1 },
|
|
8
|
+
"lines": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"minProperties": 1,
|
|
11
|
+
"additionalProperties": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"roots": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"minItems": 1,
|
|
17
|
+
"maxItems": 64,
|
|
18
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
19
|
+
},
|
|
20
|
+
"forbidden_surfaces": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"maxItems": 256,
|
|
23
|
+
"items": { "type": "string", "pattern": "^[^/]" }
|
|
24
|
+
},
|
|
25
|
+
"forbidden_absolute": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"maxItems": 256,
|
|
28
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
29
|
+
},
|
|
30
|
+
"read_roots": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"maxItems": 64,
|
|
33
|
+
"items": { "type": "string", "pattern": "^/" }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": ["roots", "forbidden_surfaces"],
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"required": ["version", "lines"],
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|