Files
quixos-nix-helpers/PORTABLE_ARTIFACTS.md

11 KiB

Checked capability and component artifacts

This is the current helper/output format, not the core package abstraction. See the architecture and current gaps. In particular, the universal artifact inventory and component-only omission of a server are implementation gaps to resolve, not requirements for arbitrary package servers.

mkCaminoArtifacts is the fresh package builder. It regenerates candidate bindings, typechecks the entry's dependency closure, bundles declared targets, generates a process service when requested, and emits content manifests and receipts. It has no migration step or package-defined shell hooks.

# Inside a per-system flake output:
quixosPackages.checkedArtifacts =
  { buildContract, sourceDigest, generator, core, sharedRuntime, typescript, packageRevisionId, ... }:
  helpers.mkCaminoArtifacts {
    inherit pkgs buildContract sourceDigest core sharedRuntime typescript packageRevisionId;
    protocol = generator;
    src = ./.;
    portable = {
      entry = "src/portable.ts";
      bindingOutput = "src/gen/qx.ts";
      registryExport = "registry";
      targets = [ "browser" "server" ];
    };
  };

buildContract is a canonical, source-free capability slice. sourceDigest records the exact selected package source in the final installation manifest; it does not enter the reusable compilation group. Ordinary source dependencies, SDKs and React props companions remain explicit Nix inputs. Raw QX and resource lock files enter through the checked contract and evaluated recipe, and are excluded from the implementation source tree. The plan argument on the low-level helper is a composition convenience for fixtures; it is projected before building. Package recipes receive the contract directly and can be checked without a workspace.

Inside checkedArtifacts = context: ..., the package helpers expose camino.packageSet { inherit nixpkgs system context; } as the usual package set selection. It shares the workspace composition's evaluated Nixpkgs when the source content, system, and default import arguments match. Pass ordinary Nixpkgs arguments such as config or overlays to use a separate import. A direct import nixpkgs { inherit system; } also works, with additional evaluation cost. The implementation groups depend on the complete execution compiler bundle and JavaScript build drivers by content; unrelated checker CLI and publication metadata changes do not enter those inputs.

The handler entry exports registry = createRegistry(handlers) from the generated bindings. One registry contains many exports. The builder always supplies a server implementation for the generated process service. A service-only package instead omits portable and supplies service.entry, an ordinary JavaScript process entry using the versioned stdin/stdout protocol. Custom services own their lifecycle and may hold external resources. This first helper supports JavaScript custom service entries; other languages can emit the same checked artifact contract independently.

Additional portable source dependencies can be provided as a locked nodeModules directory. The workspace supplies the exact SDK and compiler; packages cannot replace those with their own versions. Authored source symlinks and non-bundled portable imports other than the SDK and server Node builtins are rejected. This is build closure checking, not a purity proof or security sandbox.

mkCaminoYarnDependencies installs the locked dependency recipe and exports its installed Node dependency tree, including native package results and executable links. Generated Yarn install/linker state stays outside runtime outputs. Yarn describes its install-state file as an optimization cache in its configuration reference. This helper output is a dependency directory for a build, not a standalone Package or a runnable Yarn project. Its devShell supplies freestanding Yarn for editable projects; do not run Yarn against the readonly dependency output. Installation discards its disposable archive cache and streams dependencies into the output while retiring staged files, including across separate build/store mounts. It preserves package layout, native results, hidden data and executable links without holding two complete dependency trees. The maintained camino-yarn-dependencies check covers transfer and runtime layout; consuming Studio and Package-builder checks retain real locked-installation coverage.

Use the shared installDependencies assembly for consumers: authored libraries are copied into the consumer's node_modules, where Node can find their transitive dependencies, while selected SDK links retain their canonical type identity. Reserved SDK substitutions fail checks. Web Studio uses this same assembly rather than retaining a full Yarn project just to resolve UI libraries. Its dependency inputs contain manifests, lock and Yarn configuration/plugin recipe; ordinary Studio source edits do not enter that dependency derivation. Recipes with local/workspace dependencies must still include their needed source. This is language-specific build machinery; it adds no core Package file contract.

The core/Automerge bundle and Replicache bundle are built once. Browser modules import the core through a content-addressed gateway URL, independent of package identity. Each manifest covers the shared bytes it uses. The gateway retains its workspace authentication and explicit file registry; it never interprets an HTTP path as a Nix store path. Applications cannot import Replicache through the SDK.

checked-artifacts.nix evaluates a package's checkedArtifacts output without activation. During the checked Nix build, loadCheckedArtifactWorld verifies manifest bytes, the selected generator identity, and exact regenerated binding sources. Receipts are consistency evidence from a trusted Nix build, not cryptographic attestations of remote builds. The build stores the checked plan, kernel/query programs, their digests and original compiler provenance in its immutable candidate output. Runtime loading validates that output's identities, artifact contents and supported runtime contracts. It does not regenerate bindings or programs with the currently running compiler. Nix owns rebuilding and cache reuse when a toolchain input changes.

The root camino-artifact-builds check exercises actual reader/service outputs, module and process execution, browser shared imports, rejection recovery, and altered receipt rejection at the build boundary. The managed workspace host loads the resulting checked candidate before activation. No adapter pretends this ABI works in the old server dispatcher.

Component packages add components = { inherit (react) sdk runtime; entries; }. The ordinary execution candidate checker supplies the selected React build inputs when the package declares components. Each entry maps a component ID to { entry = "src/card.tsx"; export = "Card"; }. See the React SDK for bindings and hooks. CSS is emitted as a checked artifact; local image/font imports are inlined. The helper rejects undeclared external assets and substituted React/client dependencies. Component-only packages produce no backend service.

camino-component-artifacts checks bad subject/SDK/dependency/asset inputs and proves that a component-only edit preserves a mixed package's service digest. Run nix run .#camino-component-source-qualification from the monorepo to materialize separate retained Git resources and compile an ordinary Nix candidate. This is a source/build qualification harness; it does not deploy a workspace.

Restricted execution in a workspace VM

Add service.restricted = true to a checked portable package to select the pinned workerd executor. Generated bindings and the ordinary transaction-port protocol stay the same. All server exports must belong to one checked portable registry; remote exports and custom process services retain their ordinary VM execution path. The restricted build rejects Node imports and unresolved dependencies, and supplies an explicit SDK for value codecs, identities and declared graph ports.

service = {
  restricted = true;
  limits = {
    deadlineMs = 10000;
    maxBytes = 1048576;
    maxPortCalls = 1000;
    maxQueued = 8;
  };
};

Each invocation gets a fresh Worker in a reusable engine process. An engine runs one active frame; the adapter has bounded concurrency and queueing. Queue wait consumes the deadline. Overflow is an explicit RESTRICTED_QUEUE_FULL result. Cancellation kills the engine process group and removes its sockets before acknowledgement. All graph calls inherit the trusted invocation identity; authored code cannot choose another frame or workspace. Native graph grants and transaction commit remain enforced by Camino outside the guest. Initialization and synchronous loops are subject to the same deadline.

The default root/Package Invocation wall deadline is 10 seconds. The separate root CPU allowance remains at most 4 CPU seconds; extra wall time accommodates queueing and graph-port waits. Ordinary graph database transactions and idle transactions allow 30 seconds, and ordinary client HTTP requests allow 45 seconds so server failures can be returned. Nested calls inherit the remaining root budget; explicit smaller limits still apply. Restore/activation jobs retain their separate budgets. These defaults are starting limits, not latency targets.

Direct network access, native filesystem imports, ambient clock and entropy, parent environment credentials, and unlisted modules are denied. Explicit dates, Intl.Segmenter, deterministic hashes and normal JavaScript computation remain available. Host-managed HTTP bridges expose only the current frame's declared graph ports. Authored serialization is untrusted protocol input.

The packaged runtime uses the platform's Bubblewrap/seccomp launcher. Only the engine's read-only runtime closure and private broker directory are mounted; native filesystem/process/network escape probes qualify that boundary. Systemd limits the whole package in a workspace VM. Shared authority additionally charges each engine to its own cgroup before launch. No per-isolate memory protection or Cloudflare hosted side-channel defenses are claimed. See the runtime decision for the exact boundary and residual risks. A guest user can control its VM, so authoritative graph grants and credentials must remain outside it.

Qualification commands:

nix build .#checks.x86_64-linux.camino-restricted-execution
nix build .#checks.x86_64-linux.camino-restricted-package-runners
nix build .#checks.x86_64-linux.camino-restricted-execution-vm

The first uses the actual pinned engine for frame isolation, graph ports, SDK codecs, denied ambient access, cancellation and overload. The second runs ordinary package dispatch, rollback, PostgreSQL and browser replica tests with the restricted reader fixture. The VM check adds actual cgroup allocation exhaustion, neighboring-service survival, native socket egress rejection and fresh-frame timing. VM qualification is independent of hosted rollout.