spell-runtime 1.2.1 → 1.2.5

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 CHANGED
@@ -5,18 +5,19 @@ Minimal CLI runtime for SpellBundle v1.
5
5
  ## Setup
6
6
 
7
7
  - Node.js >= 20
8
- - npm
8
+ - pnpm (recommended)
9
+ - npm (supported)
9
10
 
10
11
  ```bash
11
- npm i
12
- npm run build
13
- npm test
12
+ pnpm install
13
+ pnpm run build
14
+ pnpm test
14
15
  ```
15
16
 
16
17
  Local dev:
17
18
 
18
19
  ```bash
19
- npm run dev -- --help
20
+ pnpm run dev -- --help
20
21
  ```
21
22
 
22
23
  ## Install as CLI
@@ -43,11 +44,11 @@ npm run smoke:npx
43
44
 
44
45
  ## Commands
45
46
 
46
- - `spell install <local-path>`
47
+ - `spell install <source>`
47
48
  - `spell list`
48
49
  - `spell inspect <id> [--version x.y.z]`
49
- - `spell cast <id> [--version x.y.z] [-p key=value ...] [--input input.json] [--dry-run] [--yes] [--allow-billing] [--require-signature] [--verbose] [--profile <name>]`
50
- - `spell license add <name> <token>`
50
+ - `spell cast <id> [--version x.y.z] [-p key=value ...] [--input input.json] [--dry-run] [--yes] [--allow-billing] [--allow-unsigned] [--require-signature] [--verbose] [--profile <name>]`
51
+ - `spell license add <name> <entitlement-token>`
51
52
  - `spell license list`
52
53
  - `spell license remove <name>`
53
54
  - `spell sign keygen <publisher> [--key-id default] [--out-dir .spell-keys]`
@@ -59,15 +60,19 @@ npm run smoke:npx
59
60
 
60
61
  ## Install Sources
61
62
 
62
- `spell install <local-path>` keeps the same CLI interface and now accepts:
63
+ `spell install <source>` accepts:
63
64
 
64
65
  - local bundle paths (existing behavior)
65
- - git URLs:
66
- - `https://...`
67
- - `ssh://...`
68
- - `git@...`
66
+ - pinned git URLs with an explicit ref suffix:
67
+ - `https://...#<ref>`
68
+ - `ssh://...#<ref>`
69
+ - `git@...#<ref>`
69
70
 
70
- When a git URL is provided, runtime performs a shallow clone (`git clone --depth 1`) into a temporary directory and installs from the cloned repository root.
71
+ Git sources must include `#<ref>`. If omitted, install fails with:
72
+
73
+ - `git source requires explicit ref (#<ref>)`
74
+
75
+ When a git source is provided, runtime clones the repository, checks out the requested ref, resolves the checked-out commit SHA (`git rev-parse HEAD`), and installs from that checkout.
71
76
 
72
77
  Limitations:
73
78
 
@@ -79,8 +84,17 @@ Limitations:
79
84
 
80
85
  - Spells: `~/.spell/spells/<id_key>/<version>/`
81
86
  - ID index: `~/.spell/spells/<id_key>/spell.id.txt`
87
+ - Install provenance: `~/.spell/spells/<id_key>/<version>/source.json`
82
88
  - Logs: `~/.spell/logs/<timestamp>_<id>_<version>.json`
83
- - Billing license tokens: `~/.spell/licenses/*.json`
89
+ - Billing entitlement records: `~/.spell/licenses/*.json`
90
+
91
+ `source.json` captures install provenance:
92
+
93
+ - `type`: `local` or `git`
94
+ - `source`: original install input
95
+ - `ref`: requested git ref (git installs only)
96
+ - `commit`: resolved git commit SHA (git installs only)
97
+ - `installed_at`: install timestamp (ISO-8601)
84
98
 
85
99
  `id_key` is fixed as `base64url(utf8(id))`.
86
100
 
@@ -99,16 +113,34 @@ Consistency rule:
99
113
  - bundle resolution by id (and optional version)
100
114
  - input assembly (`--input` + `-p` overrides)
101
115
  - JSON Schema validation by Ajv
102
- - optional signature verification (`--require-signature`)
116
+ - signature verification (default on; bypass only with `--allow-unsigned`)
117
+ - runtime policy guard (`~/.spell/policy.json`)
103
118
  - platform guard
104
119
  - risk guard (`high`/`critical` requires `--yes`)
105
120
  - billing guard (`billing.enabled` requires `--allow-billing`)
106
- - billing license guard (`billing.enabled` + `--allow-billing` requires a local token from `spell license add ...`)
121
+ - billing entitlement guard (`billing.enabled` + `--allow-billing` requires a matching valid entitlement from `spell license add ...`)
107
122
  - connector token guard (`CONNECTOR_<NAME>_TOKEN`)
108
123
  - execution summary output
109
124
 
110
125
  If `--dry-run` is set, command exits after summary and validation.
111
126
 
127
+ Policy file format (`~/.spell/policy.json`):
128
+
129
+ ```json
130
+ {
131
+ "version": "v1",
132
+ "default": "allow",
133
+ "publishers": { "allow": ["samples"], "deny": ["blocked"] },
134
+ "max_risk": "high",
135
+ "runtime": { "allow_execution": ["host", "docker"] }
136
+ }
137
+ ```
138
+
139
+ Notes:
140
+ - missing policy file => allow by default
141
+ - invalid policy file => `invalid policy: ...`
142
+ - policy rejection => `policy denied: <reason>`
143
+
112
144
  ## Runtime Safety Limits (v2 isolation)
113
145
 
114
146
  `cast` enforces these runtime limits (used by direct CLI casts and API-triggered casts because the API invokes `spell cast`):
@@ -129,7 +161,22 @@ Docker mode (v1) details:
129
161
  - `runtime.execution=docker` requires `runtime.docker_image`.
130
162
  - the image must provide `spell-runner` on `PATH` (this repo publishes it as a second npm bin).
131
163
  - the bundle is mounted read-only at `/spell`; the runner copies it into a writable temp workdir before executing steps.
132
- - environment variables passed from host -> container are restricted to connector tokens only (`CONNECTOR_<NAME>_TOKEN`). If your spell needs `{{ENV.*}}` for other values, provide them inside the image (or extend the runtime later).
164
+ - hardened `docker run` defaults:
165
+ - `--network none`
166
+ - `--cap-drop ALL`
167
+ - `--security-opt no-new-privileges`
168
+ - `--read-only`
169
+ - `--user 65532:65532`
170
+ - `--pids-limit 256`
171
+ - `--tmpfs /tmp:rw,noexec,nosuid,size=64m`
172
+ - hardening env overrides (all optional):
173
+ - `SPELL_DOCKER_NETWORK` (`none|bridge|host`, default `none`)
174
+ - `SPELL_DOCKER_USER` (default `65532:65532`; set empty to disable `--user`)
175
+ - `SPELL_DOCKER_READ_ONLY` (`1` default; set `0` to disable `--read-only`)
176
+ - `SPELL_DOCKER_PIDS_LIMIT` (`256` default; set `0` to disable `--pids-limit`)
177
+ - `SPELL_DOCKER_MEMORY` (default empty; when set adds `--memory`)
178
+ - `SPELL_DOCKER_CPUS` (default empty; when set adds `--cpus`)
179
+ - environment variables passed from host -> container are restricted to connector tokens (`CONNECTOR_<NAME>_TOKEN`) plus `SPELL_RUNTIME_STEP_TIMEOUT_MS`.
133
180
 
134
181
  ## Windows Policy
135
182
 
@@ -151,20 +198,22 @@ Use these `effect.type` words where possible:
151
198
  ## v1 Limitations (Intentionally Not Implemented)
152
199
 
153
200
  - name search or ambiguous resolution (id only)
154
- - registry/marketplace/license verification
201
+ - registry/marketplace integration
155
202
  - real billing execution (Stripe)
156
203
  - DAG/parallel/rollback/self-healing
157
204
  - advanced templating language (only `{{INPUT.*}}` and `{{ENV.*}}`)
158
- - docker env passthrough beyond connector tokens
205
+ - docker env passthrough beyond connector tokens and `SPELL_RUNTIME_STEP_TIMEOUT_MS`
159
206
 
160
207
  ## Signature (Sign + Verify)
161
208
 
162
- If a bundle contains `spell.sig.json`, you can require signature verification at execution time:
209
+ `spell cast` requires signature verification by default. To bypass this for unsigned bundle workflows, use:
163
210
 
164
211
  ```bash
165
- spell cast <id> --require-signature ...
212
+ spell cast <id> --allow-unsigned ...
166
213
  ```
167
214
 
215
+ `--require-signature` remains accepted for backward compatibility.
216
+
168
217
  Signing flow:
169
218
 
170
219
  ```bash
@@ -184,6 +233,40 @@ Notes:
184
233
  - publisher is derived from the spell id prefix before the first `/` (example: `samples/call-webhook` -> `samples`).
185
234
  - public key format is ed25519 `spki` DER encoded as base64url.
186
235
 
236
+ ## Entitlement Tokens (Billing)
237
+
238
+ `spell license add <name> <token>` now validates and stores signed entitlement tokens.
239
+
240
+ Token format:
241
+
242
+ - `ent1.<payloadBase64url>.<signatureBase64url>`
243
+
244
+ Payload JSON required fields:
245
+
246
+ - `version` (`"v1"`)
247
+ - `issuer` (string)
248
+ - `key_id` (string)
249
+ - `mode` (`"upfront" | "on_success" | "subscription"`)
250
+ - `currency` (string)
251
+ - `max_amount` (number)
252
+ - `not_before` (ISO string)
253
+ - `expires_at` (ISO string)
254
+
255
+ Verification rules:
256
+
257
+ - signature algorithm is ed25519
258
+ - signed message is the raw payload segment bytes (the exact payload base64url segment string bytes)
259
+ - trust source is the publisher trust store (`spell trust add ...`) keyed by `issuer + key_id`
260
+ - token must be within `not_before <= now <= expires_at`
261
+
262
+ Billing-enabled cast requires a matching currently-valid entitlement:
263
+
264
+ - entitlement `mode === manifest.billing.mode`
265
+ - entitlement `currency` equals `manifest.billing.currency` (case-insensitive)
266
+ - entitlement `max_amount >= manifest.billing.max_amount`
267
+
268
+ `spell license list` prints entitlement summary columns (`issuer`, `mode`, `currency`, `max_amount`, `expires_at`) and never prints raw tokens.
269
+
187
270
  ## Example Flow
188
271
 
189
272
  ```bash
@@ -195,6 +278,38 @@ spell cast fixtures/hello-host -p name=world
195
278
  spell log <execution-id>
196
279
  ```
197
280
 
281
+ ## OSS Release (pnpm + GitHub Actions)
282
+
283
+ Release automation is defined in:
284
+
285
+ - `.github/workflows/release.yml`
286
+
287
+ Prerequisites:
288
+
289
+ - npm account with `2FA` enabled
290
+ - GitHub repository secret `NPM_TOKEN` (publish-capable npm token)
291
+
292
+ Local release checks:
293
+
294
+ ```bash
295
+ pnpm install
296
+ pnpm run typecheck
297
+ pnpm run lint
298
+ pnpm run build
299
+ pnpm test
300
+ pnpm run pack:check
301
+ ```
302
+
303
+ Tag-based release flow:
304
+
305
+ ```bash
306
+ npm version patch
307
+ git push --follow-tags
308
+ ```
309
+
310
+ Pushing tag `vX.Y.Z` triggers GitHub Actions release and runs `npm publish`.
311
+ The workflow verifies that the git tag version matches `package.json`.
312
+
198
313
  ## Real-Use Sample Spells
199
314
 
200
315
  These are product-facing examples (separate from test fixtures):
@@ -220,12 +335,20 @@ spell cast samples/call-webhook --dry-run -p event=deploy -p source=manual -p pa
220
335
  - Button registry schema:
221
336
  - `/Users/koichinishizuka/spell-runtime/examples/button-registry.v1.schema.json`
222
337
  - Registry optional policy:
223
- - `require_signature` (when true, Execution API adds `--require-signature`)
338
+ - `require_signature`:
339
+ - `true`: Execution API enforces signature (`--require-signature`)
340
+ - `false`/omitted: Execution API opts into unsigned path (`--allow-unsigned`)
224
341
 
225
342
  ## Runtime Decision Log
226
343
 
227
344
  - `/Users/koichinishizuka/spell-runtime/docs/runtime-decisions-v1.md`
228
345
 
346
+ ## Repository Policies
347
+
348
+ - `/Users/koichinishizuka/spell-runtime/CONTRIBUTING.md`
349
+ - `/Users/koichinishizuka/spell-runtime/CODE_OF_CONDUCT.md`
350
+ - `/Users/koichinishizuka/spell-runtime/SECURITY.md`
351
+
229
352
  ## Execution API (Async)
230
353
 
231
354
  Start API server:
@@ -245,14 +368,14 @@ By default it listens on `:8787` and reads:
245
368
  - `GET /` (minimal Receipts UI)
246
369
  - `GET /ui/app.js` (UI client script)
247
370
  - `GET /api/buttons`
248
- - `GET /api/spell-executions` (`status`, `button_id`, `limit` query supported)
371
+ - `GET /api/spell-executions` (`status`, `button_id`, `tenant_id`, `limit` query supported)
249
372
  - `POST /api/spell-executions`
250
373
  - `GET /api/spell-executions/:execution_id`
251
374
 
252
375
  Optional environment variables:
253
376
  - `SPELL_API_PORT`
254
377
  - `SPELL_BUTTON_REGISTRY_PATH`
255
- - `SPELL_API_AUTH_KEYS` (comma-separated `role=token` entries; when set, `/api/*` requires auth and derives `actor_role` from token)
378
+ - `SPELL_API_AUTH_KEYS` (comma-separated `role=token` or `tenant:role=token` entries; when set, `/api/*` requires auth and derives `actor_role` + `tenant_id` from token)
256
379
  - `SPELL_API_AUTH_TOKENS` (legacy: comma-separated tokens; when set, `/api/*` requires auth but does not bind role)
257
380
  - `SPELL_API_BODY_LIMIT_BYTES`
258
381
  - `SPELL_API_EXECUTION_TIMEOUT_MS`
@@ -266,4 +389,5 @@ Security note:
266
389
  - execution logs redact secret-like keys (`token`, `authorization`, `apiKey`, etc.)
267
390
  - environment-derived secret values are masked in persisted logs
268
391
  - when auth is enabled, pass `Authorization: Bearer <token>` (or `x-api-key`) for `/api` routes
392
+ - with `SPELL_API_AUTH_KEYS`, non-admin list requests are restricted to their own tenant and cross-tenant `tenant_id` filters return `403` (`TENANT_FORBIDDEN`)
269
393
  - do not set both `SPELL_API_AUTH_KEYS` and `SPELL_API_AUTH_TOKENS` at the same time
package/README.txt CHANGED
@@ -4,19 +4,20 @@ Minimal CLI runtime for SpellBundle v1.
4
4
 
5
5
  1. Setup
6
6
  - Node.js >= 20
7
- - npm
7
+ - pnpm (recommended)
8
+ - npm (supported)
8
9
 
9
10
  Install dependencies:
10
- npm i
11
+ pnpm install
11
12
 
12
13
  Build:
13
- npm run build
14
+ pnpm run build
14
15
 
15
16
  Test:
16
- npm test
17
+ pnpm test
17
18
 
18
19
  Local dev:
19
- npm run dev -- --help
20
+ pnpm run dev -- --help
20
21
 
21
22
  Binary smoke checks:
22
23
  npm run smoke:link
@@ -30,11 +31,11 @@ Manual npx (local package):
30
31
  npx --yes --package file:. spell --help
31
32
 
32
33
  2. CLI commands
33
- - spell install <local-path>
34
+ - spell install <source>
34
35
  - spell list
35
36
  - spell inspect <id> [--version x.y.z]
36
- - spell cast <id> [--version x.y.z] [-p key=value ...] [--input input.json] [--dry-run] [--yes] [--allow-billing] [--require-signature] [--verbose] [--profile <name>]
37
- - spell license add <name> <token>
37
+ - spell cast <id> [--version x.y.z] [-p key=value ...] [--input input.json] [--dry-run] [--yes] [--allow-billing] [--allow-unsigned] [--require-signature] [--verbose] [--profile <name>]
38
+ - spell license add <name> <entitlement-token>
38
39
  - spell license list
39
40
  - spell license remove <name>
40
41
  - spell sign keygen <publisher> [--key-id default] [--out-dir .spell-keys]
@@ -45,14 +46,17 @@ Manual npx (local package):
45
46
  - spell log <execution-id>
46
47
 
47
48
  2.1 Install sources
48
- - spell install <local-path> keeps the same CLI interface and now accepts:
49
+ - spell install <source> accepts:
49
50
  - local bundle paths (existing behavior)
50
- - git URLs:
51
- - https://...
52
- - ssh://...
53
- - git@...
51
+ - pinned git URLs with explicit refs:
52
+ - https://...#<ref>
53
+ - ssh://...#<ref>
54
+ - git@...#<ref>
54
55
 
55
- When a git URL is provided, runtime performs a shallow clone (git clone --depth 1) into a temporary directory and installs from the cloned repository root.
56
+ Git sources must include #<ref>. If omitted, install fails with:
57
+ git source requires explicit ref (#<ref>)
58
+
59
+ When a git source is provided, runtime clones the repository, checks out the requested ref, resolves the checked-out commit SHA (git rev-parse HEAD), and installs from that checkout.
56
60
 
57
61
  Limitations:
58
62
  - git must be installed and available on PATH.
@@ -62,8 +66,16 @@ Limitations:
62
66
  3. Storage layout
63
67
  - Spells: ~/.spell/spells/<id_key>/<version>/
64
68
  - ID index: ~/.spell/spells/<id_key>/spell.id.txt
69
+ - Install provenance: ~/.spell/spells/<id_key>/<version>/source.json
65
70
  - Logs: ~/.spell/logs/<timestamp>_<id>_<version>.json
66
- - Billing license tokens: ~/.spell/licenses/*.json
71
+ - Billing entitlement records: ~/.spell/licenses/*.json
72
+
73
+ source.json captures install provenance:
74
+ - type: local or git
75
+ - source: original install input
76
+ - ref: requested git ref (git installs only)
77
+ - commit: resolved git commit SHA (git installs only)
78
+ - installed_at: install timestamp (ISO-8601)
67
79
 
68
80
  id_key is fixed as base64url(utf8(id)).
69
81
  - id is the logical identifier (display, package identity).
@@ -78,16 +90,31 @@ Cast performs these checks before execution:
78
90
  - Bundle resolution by id (and optional version)
79
91
  - Input assembly (--input + -p overrides)
80
92
  - JSON Schema validation by Ajv
81
- - Optional signature verification (--require-signature)
93
+ - Signature verification (default on; bypass only with --allow-unsigned)
94
+ - Runtime policy guard (~/.spell/policy.json)
82
95
  - Platform guard
83
96
  - Risk guard (high/critical requires --yes)
84
97
  - Billing guard (billing.enabled requires --allow-billing)
85
- - Billing license guard (billing.enabled + --allow-billing requires a local token from spell license add ...)
98
+ - Billing entitlement guard (billing.enabled + --allow-billing requires a matching valid entitlement from spell license add ...)
86
99
  - Connector token guard (CONNECTOR_<NAME>_TOKEN)
87
100
  - Execution summary output
88
101
 
89
102
  If --dry-run is set, command exits after summary and validation.
90
103
 
104
+ Policy file format (~/.spell/policy.json):
105
+ {
106
+ "version": "v1",
107
+ "default": "allow",
108
+ "publishers": { "allow": ["samples"], "deny": ["blocked"] },
109
+ "max_risk": "high",
110
+ "runtime": { "allow_execution": ["host", "docker"] }
111
+ }
112
+
113
+ Notes:
114
+ - missing policy file => allow by default
115
+ - invalid policy file => invalid policy: ...
116
+ - policy rejection => policy denied: <reason>
117
+
91
118
  4.1 Runtime safety limits (v2 isolation)
92
119
  cast enforces these runtime limits (for direct CLI casts and API-triggered casts, because the API invokes spell cast):
93
120
  - SPELL_RUNTIME_INPUT_MAX_BYTES (default 65536): max bytes for merged cast input (--input + -p overrides)
@@ -103,7 +130,22 @@ Docker mode (v1) details:
103
130
  - runtime.execution=docker requires runtime.docker_image.
104
131
  - the image must provide spell-runner on PATH (this repo publishes it as a second npm bin).
105
132
  - the bundle is mounted read-only at /spell; the runner copies it into a writable temp workdir before executing steps.
106
- - env vars passed from host -> container are restricted to connector tokens only (CONNECTOR_<NAME>_TOKEN). If your spell needs {{ENV.*}} for other values, provide them inside the image (or extend the runtime later).
133
+ - hardened docker run defaults:
134
+ - --network none
135
+ - --cap-drop ALL
136
+ - --security-opt no-new-privileges
137
+ - --read-only
138
+ - --user 65532:65532
139
+ - --pids-limit 256
140
+ - --tmpfs /tmp:rw,noexec,nosuid,size=64m
141
+ - hardening env overrides (all optional):
142
+ - SPELL_DOCKER_NETWORK (none|bridge|host, default none)
143
+ - SPELL_DOCKER_USER (default 65532:65532; set empty to disable --user)
144
+ - SPELL_DOCKER_READ_ONLY (1 default; set 0 to disable --read-only)
145
+ - SPELL_DOCKER_PIDS_LIMIT (256 default; set 0 to disable --pids-limit)
146
+ - SPELL_DOCKER_MEMORY (default empty; when set adds --memory)
147
+ - SPELL_DOCKER_CPUS (default empty; when set adds --cpus)
148
+ - env vars passed from host -> container are restricted to connector tokens (CONNECTOR_<NAME>_TOKEN) plus SPELL_RUNTIME_STEP_TIMEOUT_MS.
107
149
 
108
150
  6. Windows policy
109
151
  - host mode does not assume bash/sh.
@@ -121,15 +163,17 @@ Use these effect.type words where possible:
121
163
 
122
164
  8. v1 limitations (intentionally not implemented)
123
165
  - name search or ambiguous resolution (id only)
124
- - registry/marketplace/license verification
166
+ - registry/marketplace integration
125
167
  - real billing execution (Stripe)
126
168
  - DAG/parallel/rollback/self-healing
127
169
  - advanced templating language (only {{INPUT.*}} and {{ENV.*}})
128
- - docker env passthrough beyond connector tokens
170
+ - docker env passthrough beyond connector tokens and SPELL_RUNTIME_STEP_TIMEOUT_MS
129
171
 
130
172
  8.1 Signature (sign + verify)
131
- If a bundle contains spell.sig.json, you can require signature verification at execution time:
132
- spell cast <id> --require-signature ...
173
+ spell cast requires signature verification by default. To bypass this for unsigned bundle workflows:
174
+ spell cast <id> --allow-unsigned ...
175
+
176
+ --require-signature remains accepted for backward compatibility.
133
177
 
134
178
  Signing flow:
135
179
  spell sign keygen samples --key-id default --out-dir .spell-keys
@@ -145,6 +189,35 @@ Notes:
145
189
  - publisher is derived from the spell id prefix before the first / (example: samples/call-webhook -> samples).
146
190
  - public key format is ed25519 spki DER encoded as base64url.
147
191
 
192
+ 8.2 Entitlement tokens (billing)
193
+ spell license add <name> <token> now validates and stores signed entitlement tokens.
194
+
195
+ Token format:
196
+ - ent1.<payloadBase64url>.<signatureBase64url>
197
+
198
+ Payload JSON required fields:
199
+ - version ("v1")
200
+ - issuer (string)
201
+ - key_id (string)
202
+ - mode ("upfront" | "on_success" | "subscription")
203
+ - currency (string)
204
+ - max_amount (number)
205
+ - not_before (ISO string)
206
+ - expires_at (ISO string)
207
+
208
+ Verification rules:
209
+ - signature algorithm: ed25519
210
+ - signed message: raw payload segment bytes (exact payload base64url segment string bytes)
211
+ - trust source: publisher trust store (spell trust add ...) keyed by issuer + key_id
212
+ - token must be within not_before <= now <= expires_at
213
+
214
+ Billing-enabled cast requires a matching currently-valid entitlement:
215
+ - entitlement mode equals manifest.billing.mode
216
+ - entitlement currency equals manifest.billing.currency (case-insensitive)
217
+ - entitlement max_amount >= manifest.billing.max_amount
218
+
219
+ spell license list prints entitlement summary columns (issuer/mode/currency/max_amount/expires_at) and does not print raw tokens.
220
+
148
221
  9. Example flow
149
222
  1) Install a local fixture
150
223
  spell install ./fixtures/spells/hello-host
@@ -164,6 +237,29 @@ Notes:
164
237
  6) Show execution log
165
238
  spell log <execution-id>
166
239
 
240
+ 9.1 OSS release (pnpm + GitHub Actions)
241
+ Automation file:
242
+ .github/workflows/release.yml
243
+
244
+ Prerequisites:
245
+ - npm account with 2FA enabled
246
+ - GitHub repository secret NPM_TOKEN (publish-capable npm token)
247
+
248
+ Local release checks:
249
+ pnpm install
250
+ pnpm run typecheck
251
+ pnpm run lint
252
+ pnpm run build
253
+ pnpm test
254
+ pnpm run pack:check
255
+
256
+ Tag-based release flow:
257
+ npm version patch
258
+ git push --follow-tags
259
+
260
+ Tag push vX.Y.Z triggers GitHub Actions release and runs npm publish.
261
+ The workflow verifies that tag version matches package.json.
262
+
167
263
  10. UI connection spec
168
264
  - Decision-complete button integration spec:
169
265
  /Users/koichinishizuka/spell-runtime/docs/ui-connection-spec-v1.md
@@ -172,7 +268,8 @@ Notes:
172
268
  - Button registry schema:
173
269
  /Users/koichinishizuka/spell-runtime/examples/button-registry.v1.schema.json
174
270
  - Registry optional policy:
175
- require_signature (when true, Execution API adds --require-signature)
271
+ require_signature=true: Execution API enforces signature (--require-signature)
272
+ require_signature=false or omitted: Execution API opts into unsigned path (--allow-unsigned)
176
273
 
177
274
  11. Install from npm
178
275
  Global install:
@@ -195,6 +292,11 @@ Quick try:
195
292
  13. Runtime decision log
196
293
  - /Users/koichinishizuka/spell-runtime/docs/runtime-decisions-v1.md
197
294
 
295
+ 13.1 Repository policies
296
+ - /Users/koichinishizuka/spell-runtime/CONTRIBUTING.md
297
+ - /Users/koichinishizuka/spell-runtime/CODE_OF_CONDUCT.md
298
+ - /Users/koichinishizuka/spell-runtime/SECURITY.md
299
+
198
300
  14. Execution API (async)
199
301
  Start:
200
302
  npm run api:dev
@@ -211,14 +313,14 @@ Defaults:
211
313
  GET /
212
314
  GET /ui/app.js
213
315
  GET /api/buttons
214
- GET /api/spell-executions (status/button_id/limit query supported)
316
+ GET /api/spell-executions (status/button_id/tenant_id/limit query supported)
215
317
  POST /api/spell-executions
216
318
  GET /api/spell-executions/:execution_id
217
319
 
218
320
  Optional environment variables:
219
321
  - SPELL_API_PORT
220
322
  - SPELL_BUTTON_REGISTRY_PATH
221
- - SPELL_API_AUTH_KEYS (comma-separated role=token entries; when set, /api/* requires auth and derives actor_role from token)
323
+ - SPELL_API_AUTH_KEYS (comma-separated role=token or tenant:role=token entries; when set, /api/* requires auth and derives actor_role + tenant_id from token)
222
324
  - SPELL_API_AUTH_TOKENS (legacy: comma-separated tokens; when set, /api/* requires auth but does not bind role)
223
325
  - SPELL_API_BODY_LIMIT_BYTES
224
326
  - SPELL_API_EXECUTION_TIMEOUT_MS
@@ -232,4 +334,5 @@ Security note:
232
334
  - execution logs redact secret-like keys (token, authorization, apiKey, etc.)
233
335
  - environment-derived secret values are masked in persisted logs
234
336
  - when auth is enabled, pass Authorization: Bearer <token> (or x-api-key) for /api routes
337
+ - with SPELL_API_AUTH_KEYS, non-admin list requests are restricted to their own tenant and cross-tenant tenant_id filters return 403 (TENANT_FORBIDDEN)
235
338
  - do not set both SPELL_API_AUTH_KEYS and SPELL_API_AUTH_TOKENS at the same time