nawabari 0.7.1 → 0.8.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 +229 -29
- package/dist/cli-command-registry.d.ts +44 -0
- package/dist/cli-command-registry.js +488 -0
- package/dist/cli-command-registry.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +236 -579
- package/dist/cli.js.map +1 -1
- package/dist/contract.d.ts +6 -0
- package/dist/contract.js +261 -192
- package/dist/contract.js.map +1 -1
- package/dist/domain/cgroups-v2.d.ts +77 -0
- package/dist/domain/cgroups-v2.js +434 -0
- package/dist/domain/cgroups-v2.js.map +1 -0
- package/dist/domain/doctor.d.ts +4 -1
- package/dist/domain/doctor.js +20 -13
- package/dist/domain/doctor.js.map +1 -1
- package/dist/domain/errors.d.ts +1 -1
- package/dist/domain/errors.js.map +1 -1
- package/dist/domain/landlock.d.ts +55 -0
- package/dist/domain/landlock.js +279 -0
- package/dist/domain/landlock.js.map +1 -0
- package/dist/domain/runtime.d.ts +12 -0
- package/dist/domain/runtime.js +50 -0
- package/dist/domain/runtime.js.map +1 -0
- package/dist/domain/sandbox-launcher.d.ts +26 -0
- package/dist/domain/sandbox-launcher.js +312 -23
- package/dist/domain/sandbox-launcher.js.map +1 -1
- package/dist/domain/sandbox-seccomp.d.ts +46 -0
- package/dist/domain/sandbox-seccomp.js +302 -0
- package/dist/domain/sandbox-seccomp.js.map +1 -0
- package/dist/domain/sandbox.d.ts +45 -1
- package/dist/domain/sandbox.js +176 -25
- package/dist/domain/sandbox.js.map +1 -1
- package/dist/domain/session-backend.d.ts +3 -1
- package/dist/domain/session-backend.js +153 -1
- package/dist/domain/session-backend.js.map +1 -1
- package/dist/domain/session.d.ts +124 -1
- package/dist/domain/session.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/failure-code-vocabulary.d.ts +20 -0
- package/dist/failure-code-vocabulary.js +216 -0
- package/dist/failure-code-vocabulary.js.map +1 -0
- package/dist/git.js +92 -6
- package/dist/git.js.map +1 -1
- package/dist/operation-authorization.d.ts +1 -1
- package/dist/registry/lock.d.ts +8 -0
- package/dist/registry/lock.js +38 -20
- package/dist/registry/lock.js.map +1 -1
- package/dist/repository-evidence.d.ts +16 -0
- package/dist/repository-evidence.js.map +1 -1
- package/dist/resource-claims.d.ts +2 -2
- package/dist/resource-claims.js +96 -27
- package/dist/resource-claims.js.map +1 -1
- package/dist/session-lifecycle-actions.d.ts +63 -0
- package/dist/session-lifecycle-actions.js +108 -0
- package/dist/session-lifecycle-actions.js.map +1 -0
- package/dist/session-lifecycle-classification.d.ts +67 -0
- package/dist/session-lifecycle-classification.js +355 -0
- package/dist/session-lifecycle-classification.js.map +1 -0
- package/dist/session-registry.d.ts +128 -2
- package/dist/session-registry.js +904 -80
- package/dist/session-registry.js.map +1 -1
- package/package.json +25 -25
package/README.md
CHANGED
|
@@ -17,9 +17,11 @@ authority (`src/domain/sandbox.ts`, contract
|
|
|
17
17
|
resolved through the authoritative registry/guard path to a typed sandbox
|
|
18
18
|
execution request; it does not create a second session identity. A
|
|
19
19
|
machine-readable capability/doctor report distinguishes required Linux
|
|
20
|
-
primitives (bubblewrap, user/mount/PID/IPC/UTS namespaces
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
primitives (bubblewrap, user/mount/PID/IPC/UTS namespaces, the versioned
|
|
21
|
+
seccomp baseline, and capability reduction) from optional defense-in-depth
|
|
22
|
+
primitives (cgroups v2 and Landlock). The sandbox report exposes Landlock's
|
|
23
|
+
observed ABI, support, and effective state (`available`, `enforced`,
|
|
24
|
+
`reduced-defense`, `incompatible`, or `error`). When protected execution is requested and a required capability
|
|
23
25
|
is unavailable or the platform is unsupported, resolution fails closed and
|
|
24
26
|
never returns a request that claims the legacy unsandboxed path is
|
|
25
27
|
protected. The lower-level contract remains responsible only for capability
|
|
@@ -44,15 +46,43 @@ and pnpm's user bin when present) are read-only; credentials and the rest of
|
|
|
44
46
|
the host HOME are not mounted. `/dev`, system certificates/configuration, and
|
|
45
47
|
the detected runtime are explicit read-only/runtime inputs.
|
|
46
48
|
|
|
49
|
+
When the host exposes a compatible Landlock ABI, the protected launcher applies
|
|
50
|
+
a rule set derived from this same topology inside bubblewrap. An unavailable
|
|
51
|
+
or incompatible optional ABI leaves bubblewrap active and reports
|
|
52
|
+
`reduced-defense`; an adapter setup failure fails closed, and a profile that
|
|
53
|
+
explicitly requires Landlock also fails closed when its adapter cannot be
|
|
54
|
+
established. Setup diagnostics are bounded and no ambient/unsandboxed retry is
|
|
55
|
+
attempted.
|
|
56
|
+
|
|
47
57
|
On standalone Linux the profile uses existing `/usr`, `/bin`, `/lib*` and
|
|
48
58
|
selected `/etc` paths only when present. On NixOS it additionally selects
|
|
49
59
|
`/nix/store`, `/run/current-system`, `/run/wrappers`, and the per-user profile
|
|
50
|
-
when present;
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
when present; broad FHS views (`/usr`, `/bin`, `/lib*`) are not selected when
|
|
61
|
+
the NixOS closure roots are present. Missing required paths or namespace
|
|
62
|
+
support produces a stable capability/topology error. The protected child uses
|
|
63
|
+
`nawabari.seccomp.v1`, a compatibility-first deny-list whose policy denials
|
|
64
|
+
return `EPERM` rather than hanging or terminating ordinary development
|
|
65
|
+
subprocess trees. Ambient capabilities are empty (`--cap-drop ALL`). Network
|
|
66
|
+
remains inherited by design.
|
|
67
|
+
|
|
68
|
+
The canonical Mottainai NixOS Runtime fixture can run the opt-in compatibility
|
|
69
|
+
conformance matrix as its unprivileged repository principal:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
NAWABARI_NIXOS_RUNTIME_CONFORMANCE=1 \
|
|
73
|
+
node --test --import tsx src/domain/nixos-runtime-compat.test.ts
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This invokes the normal `session run` CLI route and production launcher. It
|
|
77
|
+
checks explicit NixOS closure paths, private versus repository-shared HOME
|
|
78
|
+
state, Git/checkpoint authority, representative toolchain subprocesses, and
|
|
79
|
+
sequential/concurrent session isolation. It does not create a test-only
|
|
80
|
+
sandbox or treat Mottainai state as Nawabari authority.
|
|
53
81
|
|
|
54
82
|
## Install
|
|
55
83
|
|
|
84
|
+
Nawabari requires Node.js >=24.
|
|
85
|
+
|
|
56
86
|
```bash
|
|
57
87
|
npm install -g nawabari
|
|
58
88
|
```
|
|
@@ -82,6 +112,17 @@ result-schema versions, identity fields, and stable `failure_codes`. The
|
|
|
82
112
|
package version is release metadata; it is not a substitute for the
|
|
83
113
|
machine-contract identifier.
|
|
84
114
|
|
|
115
|
+
The `protected-execution` capability separately advertises the versioned
|
|
116
|
+
`nawabari.sandbox-execution.v1` contract, its required and optional host
|
|
117
|
+
capabilities, the canonical `session run` entry point and `session exec` alias,
|
|
118
|
+
`network_mode: "inherited"`, and fail-closed behavior. Use
|
|
119
|
+
`nawabari doctor --json` to inspect the current host without creating or
|
|
120
|
+
selecting a session. Its `sandbox` report is produced by the same
|
|
121
|
+
`sandboxDoctorReport` authority used when resolving protected execution and
|
|
122
|
+
contains `platform_supported`, per-capability status, `ready`, and
|
|
123
|
+
`missing_required`. An unavailable required capability never implies an
|
|
124
|
+
ambient protected fallback.
|
|
125
|
+
|
|
85
126
|
The `resource-claims` capability additionally exposes a machine-readable
|
|
86
127
|
`claim_set_replacement` object (`commands`, `atomic: true`,
|
|
87
128
|
`pairing: "adjacent-resource-mode"`, `idempotent_retry: true`,
|
|
@@ -126,8 +167,8 @@ The result schemas expose the following identities:
|
|
|
126
167
|
| claims | `claim_id`, `session_id`, `resource`, `mode`, `claim_set_generation`, `previous_claim_set_generation` |
|
|
127
168
|
| authorization | `operation`, `allowed`, `code`, `claim_ids` |
|
|
128
169
|
| checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
|
|
129
|
-
| repository evidence | `session_id`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash`
|
|
130
|
-
| bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `patch`, `evidence_hash`
|
|
170
|
+
| repository evidence | `session_id`, `session_updated_at`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
|
|
171
|
+
| bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `diagnostics`, `patch`, `evidence_hash` |
|
|
131
172
|
| commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
|
|
132
173
|
| reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
|
|
133
174
|
|
|
@@ -159,13 +200,24 @@ New sessions persist the exact creation/base revision as `base_revision`;
|
|
|
159
200
|
legacy records that lack this field report `base_revision: null` and
|
|
160
201
|
`base_revision_proven: false` rather than inferring it from a mutable ref.
|
|
161
202
|
|
|
203
|
+
`session_updated_at` is the UTC timestamp of the last authoritative
|
|
204
|
+
session-state mutation, not a general Git filesystem mtime. A successful
|
|
205
|
+
Nawabari-managed `commit` is such a mutation, so the registry timestamp is
|
|
206
|
+
persisted before the command returns and a subsequent snapshot reports it with
|
|
207
|
+
the resulting `head`. Git changes made outside Nawabari do not advance this
|
|
208
|
+
field; consumers must use the Git-observed fields for those changes.
|
|
209
|
+
|
|
162
210
|
`diff` requires at least one explicit concrete path and never accepts a glob or
|
|
163
211
|
an empty repository-wide selection. Stats are returned by default. Patch text
|
|
164
212
|
requires `--patch` and is bounded to at most 64 paths, 64 KiB, and 128 hunks;
|
|
165
213
|
the caller may request smaller limits. Unrepresentable Git observations fail
|
|
166
214
|
with `GIT_STATE_AMBIGUOUS`; a requested path whose stat is not exposed by Git
|
|
167
215
|
remains in the result with `available: false` and makes snapshot evidence
|
|
168
|
-
`complete: false`, so no path silently disappears.
|
|
216
|
+
`complete: false`, so no path silently disappears. Such paths include a
|
|
217
|
+
bounded `diagnostics` entry; a target directly observed as untracked reports
|
|
218
|
+
`reason: UNTRACKED_TARGET`, while other unavailable-stat causes remain
|
|
219
|
+
`STAT_UNAVAILABLE`. These diagnostics are read-only and do not stage or add
|
|
220
|
+
files.
|
|
169
221
|
|
|
170
222
|
## Session lifecycle
|
|
171
223
|
|
|
@@ -216,8 +268,9 @@ destructive cleanup. A clean close releases only the owned worktree and
|
|
|
216
268
|
branch, and repeating close is idempotent. `gc` detects stale or interrupted
|
|
217
269
|
sessions; `--apply` uses the same close safety checks and reports blocked
|
|
218
270
|
sessions instead of guessing. `gc --dry-run` performs the same non-mutating
|
|
219
|
-
cleanup preflight
|
|
220
|
-
|
|
271
|
+
cleanup preflight, reports age/physical/lifecycle suspicion separately from
|
|
272
|
+
destructive eligibility, and includes stable blocker codes and
|
|
273
|
+
`recovery_hints` for every eligible candidate that is not safe. Cleanup revalidates the physical worktree,
|
|
221
274
|
branch, and `HEAD` observations immediately before each destructive Git
|
|
222
275
|
operation.
|
|
223
276
|
|
|
@@ -238,12 +291,15 @@ history view; closed records remain persisted and are never silently deleted
|
|
|
238
291
|
by listing or cleanup.
|
|
239
292
|
|
|
240
293
|
`gc` stale eligibility is separate from closed-history retention. Its default
|
|
241
|
-
threshold is 24 hours (`86,400,000` ms), measured from persisted `updated_at
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
294
|
+
threshold is 24 hours (`86,400,000` ms), measured from persisted `updated_at`.
|
|
295
|
+
Elapsed age is diagnostic suspicion only: it never authorizes destructive
|
|
296
|
+
cleanup for a physically healthy active session. Records already in `stale` or
|
|
297
|
+
`closing` state are eligible, as is an otherwise live record when Git reports
|
|
298
|
+
its registered worktree as safely prunable and missing. Ambiguous physical
|
|
299
|
+
state remains ineligible and fail-closed. `gc --dry-run` exposes suspicion and
|
|
300
|
+
destructive eligibility/reason separately for each candidate; `gc --apply`
|
|
301
|
+
uses only eligible candidates. Closed records are never stale cleanup
|
|
302
|
+
candidates.
|
|
247
303
|
|
|
248
304
|
`doctor` includes a non-destructive `reconciliation` check. It reports
|
|
249
305
|
registry/Git ownership drift, including missing or prunable worktrees and
|
|
@@ -258,8 +314,18 @@ code. Claim JSON exposes `schema_version`, `claim_id`, `session_id`, the
|
|
|
258
314
|
repository/worktree identities, canonical `resource`, `mode`, and timestamps.
|
|
259
315
|
The claim schema version is `2` and supports `read`, `write`, and
|
|
260
316
|
`exclusive-write`. Schema v1 records use different overlap semantics and are
|
|
261
|
-
not interpreted implicitly
|
|
262
|
-
`
|
|
317
|
+
not interpreted implicitly. If an upgraded repository reports
|
|
318
|
+
`UNSUPPORTED_CLAIM_SCHEMA_VERSION`, run the public migration command:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
git nawabari migrate --json
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Migration validates the complete legacy registry under the registry lock and
|
|
325
|
+
rewrites it with an atomic replace. It is idempotent and retry-safe; ambiguous
|
|
326
|
+
or corrupt state is rejected with bounded diagnostics. Do not hand-edit or
|
|
327
|
+
delete the registry. Embedding callers may use the same authority through
|
|
328
|
+
`SessionRegistry.migrate()`.
|
|
263
329
|
|
|
264
330
|
```bash
|
|
265
331
|
git nawabari session claim --session "$NAWABARI_SESSION_ID" \
|
|
@@ -337,6 +403,14 @@ garbage-collecting a session releases its claims; no separate claim registry
|
|
|
337
403
|
or claim lock exists. Claims describe ownership state only and do not provide
|
|
338
404
|
OS-level filesystem observation or a filesystem sandbox.
|
|
339
405
|
|
|
406
|
+
For an existing path, canonicalization follows the physical directory entry,
|
|
407
|
+
so a case-insensitive filesystem cannot give `README.md` and `readme.md`
|
|
408
|
+
independent claim identities. This is determined from the filesystem entry
|
|
409
|
+
itself; paths that do not exist yet, and wildcard portions of a claim, retain
|
|
410
|
+
their exact lexical case because their future physical identity is unknown.
|
|
411
|
+
On a case-sensitive filesystem an alternate-case spelling is therefore a
|
|
412
|
+
distinct (possibly future) path, and no global lowercasing is applied.
|
|
413
|
+
|
|
340
414
|
An ordinary source change uses `write` and can proceed while another session
|
|
341
415
|
holds a `read` declaration:
|
|
342
416
|
|
|
@@ -389,6 +463,16 @@ identifiers, in addition to the human-readable `message`. JSON and human
|
|
|
389
463
|
output always render the identical underlying result; only the formatting
|
|
390
464
|
differs.
|
|
391
465
|
|
|
466
|
+
Lifecycle diagnostics additionally expose `next_action` (and the bounded
|
|
467
|
+
`next_actions` list) as typed, non-mutating caller actions. The action schema
|
|
468
|
+
is versioned independently and currently includes `retain-session`,
|
|
469
|
+
`supply-exact-integrated-revision`,
|
|
470
|
+
`retry-close-with-bounded-integration-fetch`, `discard-session`, and
|
|
471
|
+
`reconcile-physical-state`. A discard action always carries explicit intent;
|
|
472
|
+
ambiguous or terminal states never advertise destructive actions. These
|
|
473
|
+
fields are additive to `session-diagnostic.v1`, so existing consumers may
|
|
474
|
+
continue using `safe_actions`.
|
|
475
|
+
|
|
392
476
|
- **`RESOURCE_CLAIM_CONFLICT`** (`session claim`/`session update`,
|
|
393
477
|
`authorize`, `guard --operation`) reports the blocking claim
|
|
394
478
|
(`ownerClaimId`, `ownerResource`, `ownerMode`) and the blocking session's
|
|
@@ -521,6 +605,16 @@ successful commit — it fails with `COMMIT_RESULT_DIVERGED`, which retains the
|
|
|
521
605
|
resulting `commitSha` (the Git commit already happened) alongside the
|
|
522
606
|
authorized, actual, and divergent path sets for recovery/reconciliation.
|
|
523
607
|
|
|
608
|
+
If Git reports a bounded transport failure (timeout, output limit, or spawn
|
|
609
|
+
failure) after the commit invocation, Nawabari re-reads the local `HEAD` and
|
|
610
|
+
that commit's bounded changed-path set before classifying the outcome. A
|
|
611
|
+
failure whose `HEAD` is unchanged is reported with `outcome: "proven-absent"`
|
|
612
|
+
and `retrySafe: true`; a matching new commit is returned as a successful
|
|
613
|
+
result with `reconciliation.outcome: "proven-committed"` and its resulting
|
|
614
|
+
SHA; if either observation is unavailable or does not match the authorized
|
|
615
|
+
paths, the failure carries `outcome: "unresolved"` and `retrySafe: false`.
|
|
616
|
+
Unresolved outcomes never authorize a blind retry.
|
|
617
|
+
|
|
524
618
|
```bash
|
|
525
619
|
git nawabari commit --session "$NAWABARI_SESSION_ID" \
|
|
526
620
|
--message 'record the local change' --resource src/example.ts --json
|
|
@@ -546,12 +640,19 @@ Governed push requires explicit claim-covered resources and an explicit
|
|
|
546
640
|
`--remote`/`--branch` target. Existing upstream and local/remote relation are
|
|
547
641
|
inspected before mutation. A missing upstream requires `--create-upstream`;
|
|
548
642
|
behind or diverged history requires explicit `--force`. Force pushes use an
|
|
549
|
-
exact `--force-with-lease` bound to the observed remote branch SHA
|
|
550
|
-
pushes
|
|
551
|
-
|
|
552
|
-
|
|
643
|
+
exact `--force-with-lease` bound to the observed remote branch SHA. Ordinary
|
|
644
|
+
pushes use the same exact-generation lease, while force authorization remains
|
|
645
|
+
separate. A new remote branch uses an empty lease, requiring the target ref to
|
|
646
|
+
remain absent. Nawabari fetches only the explicit remote branch into a
|
|
647
|
+
disposable ref when local ancestry is missing; it does not update tracking
|
|
648
|
+
refs or fetch unrelated branches/tags.
|
|
553
649
|
The JSON result includes the immutable `source_sha`, explicit `target_ref`,
|
|
554
|
-
observed `observed_remote_sha`, and relation.
|
|
650
|
+
observed `observed_remote_sha`, and relation. If a bounded transport failure
|
|
651
|
+
may have happened after the remote mutation, Nawabari re-observes only that
|
|
652
|
+
exact remote ref. It reports `reconciliation.outcome` as `proven-pushed`,
|
|
653
|
+
`proven-absent`, or `unresolved`, with the exact precondition and post-failure
|
|
654
|
+
generation evidence. Only a proven-absent outcome is retry-safe; unresolved
|
|
655
|
+
outcomes never authorize a blind retry.
|
|
555
656
|
|
|
556
657
|
```bash
|
|
557
658
|
git nawabari push --session "$NAWABARI_SESSION_ID" \
|
|
@@ -610,11 +711,18 @@ ID, canonical worktree and branch identities, lifecycle state, and timestamps.
|
|
|
610
711
|
|
|
611
712
|
Ownership-changing writes use an exclusive repository-local lock and a synced
|
|
612
713
|
temporary file followed by atomic replacement. Concurrent creation cannot
|
|
613
|
-
silently duplicate an active worktree or branch. Lock recovery is conservative
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
714
|
+
silently duplicate an active worktree or branch. Lock recovery is conservative
|
|
715
|
+
and platform-qualified: stale-lock reclamation is supported only on Linux,
|
|
716
|
+
where the lock records a random token, PID, host, and the exact process-start
|
|
717
|
+
token from `/proc/<pid>/stat`. An owner is reclaimed only when the same host
|
|
718
|
+
proves that exact process identity is dead; elapsed age and PID liveness alone
|
|
719
|
+
are never reclaim authority. On non-Linux platforms, Node can run ordinary
|
|
720
|
+
Nawabari operations but does not provide a safe process-generation identity,
|
|
721
|
+
so stale local locks remain `LOCK_STALE` and require deliberate operator
|
|
722
|
+
remediation. Invalid, remote, or otherwise unverifiable lock metadata is never
|
|
723
|
+
stolen and fails closed. The same limitation is machine-readable under
|
|
724
|
+
`capabilities --json` at the `session-lifecycle.registry_lock_recovery`
|
|
725
|
+
contract.
|
|
618
726
|
|
|
619
727
|
### Conformance and extraction boundary
|
|
620
728
|
|
|
@@ -639,6 +747,98 @@ The relevant Mottainai #28 execution cases are mapped as follows:
|
|
|
639
747
|
Run `pnpm run test:package` to validate the exact packed tarball and its
|
|
640
748
|
installed CLI, or `pnpm run verify` for the complete local conformance gate.
|
|
641
749
|
|
|
750
|
+
## Exact packed standalone protected-execution evidence
|
|
751
|
+
|
|
752
|
+
`pnpm run test:package:protected` is the package/evidence gate for the
|
|
753
|
+
standalone protected product. It creates one exact `pnpm pack` archive, records its
|
|
754
|
+
package/version, filename, byte size, SHA-256, source revision, and host
|
|
755
|
+
identity in `test-artifacts/packed-standalone-protected-execution.json`,
|
|
756
|
+
validates the archive contents, and installs that archive into a fresh
|
|
757
|
+
temporary consumer with `npm install --offline`. The default evidence report
|
|
758
|
+
is ignored by Git; pass `--evidence-output <path>` to retain it elsewhere and
|
|
759
|
+
`--keep-tarball` to retain the exact archive for inspection.
|
|
760
|
+
|
|
761
|
+
The smoke test invokes only the installed `nawabari` bin. Its package allowlist
|
|
762
|
+
contains `dist` runtime artifacts, `README.md`, `LICENSE`, and `package.json`;
|
|
763
|
+
source, test, and script modules are rejected, and the installed manifest must
|
|
764
|
+
not declare runtime dependencies. The lifecycle proof goes through the
|
|
765
|
+
installed public CLI for `capabilities --json`, `doctor --json`, session
|
|
766
|
+
creation/resolution, `session run`, resource claims, checkpoint, commit,
|
|
767
|
+
local-bare-remote push, and close. The gate requires the protected doctor
|
|
768
|
+
report to be ready; unavailable protected execution is a failure, not a skip
|
|
769
|
+
or an ambient fallback. The fixture uses only a temporary local repository and
|
|
770
|
+
local bare remote and has no Mottainai, GitHub, `gh`, LLM, or network
|
|
771
|
+
dependency.
|
|
772
|
+
|
|
773
|
+
The ordinary `pnpm run test:package` command runs the package/install smoke
|
|
774
|
+
without requiring a real protected host, so it retains the existing
|
|
775
|
+
compatibility CI job; it still verifies fail-closed rejection when protection
|
|
776
|
+
is unavailable. The `pnpm run test:package:protected` command is the #149
|
|
777
|
+
evidence command and must be run on a supported Linux host. It requires a
|
|
778
|
+
ready protected profile and fails when that prerequisite is unavailable.
|
|
779
|
+
|
|
780
|
+
The evidence supports these boundaries only:
|
|
781
|
+
|
|
782
|
+
- Process: on Linux with the required capabilities, the canonical launcher
|
|
783
|
+
establishes bubblewrap user, mount, PID, IPC, and UTS namespaces, applies
|
|
784
|
+
the versioned seccomp profile, and drops ambient capabilities. This is not a
|
|
785
|
+
VM or a claim about every host process.
|
|
786
|
+
- Filesystem: the protected child receives the authoritative session worktree
|
|
787
|
+
read-write, private session HOME/cache/`/tmp`/`/proc`, repository-owned
|
|
788
|
+
shared HOME state, and the fixed read-only runtime/tool inputs selected by
|
|
789
|
+
the profile. Sibling worktrees and unselected host HOME paths are not part
|
|
790
|
+
of the selected topology.
|
|
791
|
+
- HOME/cache: the child sees `/home/nawabari`; private state is per session,
|
|
792
|
+
while only the repository's selected shared-home subtree is shared. Selected
|
|
793
|
+
host tool directories are read-only inputs, not the host HOME.
|
|
794
|
+
- Network: `network_mode` is `inherited`; this evidence does not prove egress
|
|
795
|
+
isolation.
|
|
796
|
+
- Linux prerequisites: use `nawabari doctor --json` on a supported Linux host.
|
|
797
|
+
The package's supported Node.js engine, Git, and required bubblewrap,
|
|
798
|
+
namespace, seccomp, and capability support must be available and ready;
|
|
799
|
+
cgroups v2 and Landlock remain profile-reported optional defenses.
|
|
800
|
+
- Failure behavior: protected resolution uses `enforce: true`; missing
|
|
801
|
+
required capability, unsupported topology, or launch failure returns a
|
|
802
|
+
bounded failure and never retries through ambient execution.
|
|
803
|
+
|
|
804
|
+
## Packed Mottainai preselected-UID handoff evidence
|
|
805
|
+
|
|
806
|
+
`pnpm run test:package:mottainai` is the required-CI external-handoff gate for
|
|
807
|
+
Issue #150. `scripts/run-mottainai-uid-handoff-gate.mjs` runs the #149
|
|
808
|
+
protected package gate exactly once, retains that exact tarball and its
|
|
809
|
+
`nawabari.packed-standalone-protected-execution.v1` evidence, and passes only
|
|
810
|
+
those exact paths into `scripts/run-mottainai-uid-handoff.mjs`, which installs
|
|
811
|
+
that same tarball into a disposable consumer. `run-mottainai-uid-handoff.mjs`
|
|
812
|
+
itself has no self-pack path: it always requires an already-produced
|
|
813
|
+
`--tarball`/`--artifact-evidence` pair, so #150 can never hide a stale or
|
|
814
|
+
mismatched artifact behind a second pack. The fixture invokes the installed
|
|
815
|
+
`session run` contract under two distinct preselected unprivileged UIDs
|
|
816
|
+
(23001 and 23002 by default) and records bounded
|
|
817
|
+
artifact/fixture/UID/session/worktree/resource/Git evidence in
|
|
818
|
+
`test-artifacts/mottainai-packed-uid-handoff.json`. This gate runs in required
|
|
819
|
+
CI on a Linux host with bubblewrap (`mottainai-uid-handoff` job).
|
|
820
|
+
|
|
821
|
+
The checked-in `scripts/test-fixtures/mottainai-preselected-uid-runner.sh` is a
|
|
822
|
+
UID-only execution adapter. A real Mottainai Runtime may supply another
|
|
823
|
+
executable with the same `--uid`, `--root`, `--cwd`, `--` argv contract:
|
|
824
|
+
|
|
825
|
+
```bash
|
|
826
|
+
node scripts/run-mottainai-uid-handoff.mjs \
|
|
827
|
+
--tarball nawabari-0.7.1.tgz \
|
|
828
|
+
--artifact-evidence test-artifacts/packed-standalone-protected-execution.json \
|
|
829
|
+
--fixture-runner /path/to/mottainai-uid-runner \
|
|
830
|
+
--uids 23001,23002 \
|
|
831
|
+
--output test-artifacts/mottainai-packed-uid-handoff.json
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
The runner supplies only the caller's OS UID/GID view. Nawabari remains the
|
|
835
|
+
sole authority for session, worktree, resource claims, protected execution,
|
|
836
|
+
checkpoint, commit, push, local Git, and close; the fixture creates no
|
|
837
|
+
repository-principal registry and passes no Mottainai task, policy, or
|
|
838
|
+
credential semantics. Identical display names, branch names, and resource
|
|
839
|
+
paths are used under different canonical repository paths, and the protected
|
|
840
|
+
child's session/cwd marker is checked to prevent principal confusion.
|
|
841
|
+
|
|
642
842
|
## Development
|
|
643
843
|
|
|
644
844
|
```bash
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type CliHelpOptionSpec = {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
readonly aliases?: readonly string[];
|
|
4
|
+
readonly alias_of?: string;
|
|
5
|
+
readonly value?: string;
|
|
6
|
+
readonly required?: boolean;
|
|
7
|
+
readonly default?: string;
|
|
8
|
+
readonly minimum?: number;
|
|
9
|
+
readonly maximum?: number;
|
|
10
|
+
readonly repeatable?: boolean;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
};
|
|
13
|
+
export type CliCommandDefinition = {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly aliases?: readonly string[];
|
|
16
|
+
readonly summary: string;
|
|
17
|
+
readonly usage: string;
|
|
18
|
+
readonly options: readonly CliHelpOptionSpec[];
|
|
19
|
+
readonly notes?: readonly string[];
|
|
20
|
+
};
|
|
21
|
+
export declare const GLOBAL_HELP_OPTIONS: readonly CliHelpOptionSpec[];
|
|
22
|
+
/**
|
|
23
|
+
* Canonical public command/discovery registry.
|
|
24
|
+
*
|
|
25
|
+
* Dispatcher implementation deliberately remains below in this module. This
|
|
26
|
+
* registry only describes the public discovery surface; aliases point at the
|
|
27
|
+
* canonical command so option metadata cannot drift between projections.
|
|
28
|
+
*/
|
|
29
|
+
export declare const CLI_COMMAND_REGISTRY: readonly CliCommandDefinition[];
|
|
30
|
+
export declare const ROOT_HELP_SPEC: CliCommandDefinition;
|
|
31
|
+
/** Resolve a public name through the one canonical registry. */
|
|
32
|
+
export declare function canonicalCommandForName(name: string): CliCommandDefinition | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Materialize public names for projections without copying option metadata.
|
|
35
|
+
* Alias entries are generated from their canonical definition, including the
|
|
36
|
+
* usage, options, and parser notes.
|
|
37
|
+
*/
|
|
38
|
+
export declare function publicCliCommandDefinitions(): readonly CliCommandDefinition[];
|
|
39
|
+
/** Stable alias for consumers such as the future dispatcher parity layer. */
|
|
40
|
+
export declare const COMMAND_REGISTRY: readonly CliCommandDefinition[];
|
|
41
|
+
/** Resolve either a canonical command or one of its public aliases. */
|
|
42
|
+
export declare function resolveCliCommandDefinition(name: string): CliCommandDefinition | undefined;
|
|
43
|
+
/** Return the complete public discovery name list in registry order. */
|
|
44
|
+
export declare function publicCliCommandNames(): readonly string[];
|