Files
deepseek-harness/native/landlock-run/AGENTS.md
kingwl 0a486f09c9 chore: adopt node-addon-landlock-run source as native/ subtree
Bring the node-addon-landlock-run tree (tag v0.0.1, commit 614f7fd) into
native/landlock-run as its source of record: launcher development happens
here, next to the harness consumers, and the standalone repository becomes
the release mirror the tree is exported to for packing and publishing
(procedure in native/README.md). The subtree keeps its own pnpm workspace
and lockfile and is NOT added to the harness workspace: harness installs,
gates, and CI never touch it. The mirror's .github/ stays out of the
subtree; a separate manually-dispatched workflow
(.github/workflows/landlock-run.yml) runs the subtree's CI legs — the
per-architecture native builds, real-kernel launcher proofs, and pack
rehearsal — adapted with working-directory/cache paths.

eslint ignores the subtree like vendor/; AGENTS.md gains the native/
layout line (+5 words on its budget ceiling).
2026-07-14 23:39:58 +08:00

5.1 KiB

AGENTS.md

This workspace builds landlock-run, a Landlock self-restrict-then-exec launcher: a small, auditable confinement binary distributed as prebuilt per-platform npm packages, plus the thin JS entry package that resolves it and speaks its CLI contract. The source of record is the deepseek-harness repository's native/landlock-run/; the node-addon-landlock-run repository is the release mirror this tree is exported to for packing and publishing (procedure: native/README.md in the harness repo). Make changes in the source of record, never only in the mirror.

Pre-release stance

The project is pre-1.0. Prefer the correct public shape over compatibility shims: if a package name, exported field, layout, or contract detail is wrong, rename it and update all references in the same change. Do not add deprecated aliases unless a stable release already needs them.

Runtime safety rules

  • Every tool must fail closed. If a ruleset cannot be created or the kernel does not enforce it, exit non-zero WITHOUT exec'ing the wrapped command. Never run unconfined as a fallback.
  • Runtime binaries and the entry packages take NO environment-variable overrides: which binary confines a process must never be decidable by the ambient environment. Test injection is by function parameter; the NALR_* prefix is for build/test orchestration only.
  • Kernel UAPI is self-defined in the C source (verbatim from the kernel headers), keeping builds independent of toolchain header vintage and making the definitions part of the audit record.
  • No libraries beyond libc, linked statically against musl. The audit surface of a tool is its C source plus the kernel's stable syscall contract.
  • The CLI contract of each tool (docs/cli-contract.md) is the cross-repo compatibility surface: argv grammar, exit codes, and report lines change only with a version bump and a changelog entry, and consumers parse them only through the entry package.
  • There is deliberately NO install-time build fallback: a host without a matching platform package gets a nonexistent launcher path, the consumer's probe fails, and the consumer falls closed — that degradation is part of the design, not a gap to fill with node-gyp.

Repository layout

packages/entry/     Published entry package: JS seam (resolve/probe/grants) + the C source.
packages/linux-*/   Published per-platform packages: one prebuilt static binary, no JavaScript.
scripts/            Build, matrix derivation, prepack gates, and release orchestration.
test/               Plain-node behavioral tests (entry seam + real-kernel launcher proofs).
docs/               Architecture, packaging, CLI contract, release, support matrix, naming.

Commands

pnpm install
pnpm build:ts        # entry packages → lib/
pnpm build:native    # this Linux architecture's binaries (needs musl-tools); fails fast elsewhere
pnpm typecheck
pnpm test            # entry tests everywhere; launcher tests need linux + built binary

Packaging invariants

  • The package matrix is explicit, checked-in metadata: packages/<name>/package.json (os, cpu), packages/<name>/prebuilds.json (the binaries that may exist there), and docs/support-matrix.md stay synchronized when the matrix changes. scripts/github-matrix.mjs derives CI and release matrices from it; nothing else enumerates platforms.
  • Platform package names contain platform only (-linux-x64), never tool variants — those stay inside prebuilds.json. Static musl linking is why there is no libc suffix: one binary serves glibc and musl distros.
  • Platform packages ship no JavaScript; the entry package resolves them to file paths. Backends prove themselves at runtime through the functional probe, never through metadata trust.
  • Builds are native-only: each architecture compiles its own binary on its own runner (CI is the builder of record); no cross toolchain enters the repo.
  • Every tarball is gated at pack time: platform packages refuse to pack without their declared binaries present, executable, and in the right ELF architecture (verify-launcher-binary.mjs), entry packages without built lib/ (verify-entry-lib.mjs), and the release pipeline byte-pins installed binaries against the workspace builds (verify-packed-install.mjs).
  • Platform tarballs are packed with npm pack, never pnpm pack: pnpm's pack path strips the executable bit (observed on 11.7.0), shipping a launcher no consumer can spawn. pack-release.mjs encodes the split; the rehearsal asserts executability of the installed copy so a regression fails loudly instead of masquerading as a non-enforcing kernel.
  • Generated artifacts stay out of git: packages/*/bin/, packages/*/lib/, dist/, .release/, *.tsbuildinfo. Ignore rules live in the ROOT .gitignore only — a package-nested ignore file can silently drop payload from tarballs.

Documentation

User-facing docs are English. Keep the README focused on install, usage, and support status; durable design decisions belong in docs/ alongside the code, and the current implemented shape belongs in docs/architecture.md.