@daevix/paperclip-plugin-daevix
v0.1.0
Published
Daevix sovereign enclave sandbox provider
Readme
paperclip-plugin-daevix — daevix sandbox-provider plugin for Paperclip
Paperclip sandbox-provider plugin that makes a daevix enclave the execution backend for Paperclip-managed agents: each turn runs inside a daevix-managed kata-clh enclave VM provisioned through the daevix environment-lease API (destroy/recreate semantics, lease-scoped egress, no inbound surface).
Provider model
- Manifest:
kind: "sandbox_provider",driverKey: "daevix", capabilityenvironment.drivers.register. - Lifecycle: per_turn — the plugin deliberately does NOT declare
supportsReusableLeasesand does not implementenvironmentResumeLease/environmentReleaseLease. The runtime acquires a fresh lease per run and destroys it at turn end (the orphan-cleanup sweep reusesenvironmentDestroyLeasefor stranded leases, including null lease ids after crashed runs). - Transport: dial_wss — the plugin declares neither
runnerWebSocketIngressnorenvironmentRunnerIngressEndpoint; Paperclip therefore launches runnerd in outbound-onlydial_wssmode. The WebSocket connect URL rides runnerd argv. - Relay auth: Zitadel JWT-profile — the plugin mints an RSA JWT-profile assertion against the configured Zitadel issuer/token endpoint to authenticate to the daevix relay; TLS to the relay is pinned via an optional CA bundle.
- Secret redaction: credentials and lease secrets are redacted before anything
crosses the agent-visible surface; the daevix API credential is a
worker-held secret-ref (
format: "secret-ref"in the configSchema). The value is resolved only inside the plugin worker at lease-operation time and never appears in lease metadata, config, logs, or anything agent-visible.
Config schema
| Field | Meaning |
|---|---|
| relayUrl | daevix enclave relay base URL (https:///wss://) |
| apiCredential | secret-ref id of the daevix API credential (worker-held) |
| imageRef | OCI image reference for the lease VM (no embedded digest) |
| imageDigest | pinned digest, sha256:<64 hex> |
| zitadelIssuer / zitadelTokenEndpoint / zitadelHostHeader | Zitadel JWT-profile issuer, token endpoint, and optional host-header override |
| relayCaBundlePath | optional path to a CA bundle for pinning relay TLS |
| workspaceRepo / workspaceRef | workspace repository + ref to materialize |
| egressAllowlist | optional advisory host:port additions (the lease's egress grant is authored by the daevix deployment's configured destination policy) |
Worker methods (v1)
Implemented: initialize/health/shutdown (SDK lifecycle),
environmentValidateConfig, environmentProbe, environmentAcquireLease,
environmentDestroyLease, environmentRealizeWorkspace, environmentExecute.
Omitted (deliberately, per the per_turn/dial_wss model): resume/release, runner ingress endpoint, interactive-setup, template, sync, login-PTY, and duplex-channel hooks.
environmentAcquireLease/environmentDestroyLease/environmentProbe call
the daevix lease API
(POST/DELETE /api/v1/environments/leases[...], problem+json errors).
environmentExecute posts to /api/v1/environments/leases/{id}/exec; the
bootstrap ticket rides the PAPERCLIP_RUNNER_BOOTSTRAP_TICKET env of the
executed command (never argv).
Install
From npm (once published; installed into Paperclip via the paperclipai plugin
install flow):
npm install @daevix/paperclip-plugin-daevixFrom a local checkout (the plugin is loaded from disk; build first —
plugin install packages dist/):
pnpm install && pnpm build
paperclipai plugin install /absolute/path/to/paperclip-plugin-daevixIf you previously installed from another path, uninstall and reinstall so the install source points at this checkout:
paperclipai plugin uninstall daevix
paperclipai plugin install /absolute/path/to/paperclip-plugin-daevixPublishing
Maintainers publish the package with:
pnpm build
npm publishCI will hold a scoped automation token for @daevix/paperclip-plugin-daevix
and publish on release. The package is not yet published to npm.
Runner image
The lease VM image (docker/daevix-runner/Dockerfile) builds ENTIRELY from
this repository against 100% stock Paperclip — no Paperclip checkout or PR
is needed anywhere.
Pinned upstream source: the first build stage (
paperclip-source) shallow-fetches the upstream Paperclip repository (https://github.com/paperclipai/paperclip.git) and checks out the revision pinned byPAPERCLIP_RUNNER_SOURCE_REVISION(a full commit SHA; the build fails closed if the argument is empty or the checked-out SHA does not match). The pin is currently0f14d261233c545aa6a8a38ec253c498a5130fff— the stock upstream master tip the historical daevix Paperclip branch forked from. The runnerd binary and the provider pack are therefore built from reviewed, unmodified upstream code only.Build (from this repo, not from Paperclip):
content_id="$(node scripts/daevix-image-content.ts)" sudo -n podman build \ --build-arg PAPERCLIP_RUNNER_CONTENT_ID="$content_id" \ --build-arg PAPERCLIP_RUNNER_SOURCE_REVISION=0f14d261233c545aa6a8a38ec253c498a5130fff \ --tag localhost/dvx-runner:dev --file docker/daevix-runner/Dockerfile .Content identity:
node scripts/daevix-image-content.ts(also exposed aspnpm test:e2e:daevix-runner:image-id) computes the audited content id from the pinned upstream revision, the digest-pinned base images, the Dockerfile syntax frontend digest, and the out-of-tree daevix build inputs (Dockerfile, entrypoint, LiteLLM bridge config + hook). It must be passed asPAPERCLIP_RUNNER_CONTENT_IDat build time and is advertised as theio.paperclip.runner.content-idlabel.Gates kept identical: the provider-pack lockfile-SHA gate (
PAPERCLIP_RUNNER_LOCK_SHA256, default matches the pinned revision'spnpm-lock.yaml), the--build-metadataverification layer (dial_ws_loopback/dial_wss/listen_ws),/bin/shpresence for lease exec, the fail-closed entrypoint self-tests (bootstrap ticket only viaPAPERCLIP_RUNNER_BOOTSTRAP_TICKETenv, wss-only connect URL, exit 64 negatives), the codex shim + LiteLLM responses→chat wire bridge, and fully-qualified base image refs.Rationale: maintaining the image out-of-tree in this repo means the daevix integration operates against stock Paperclip with zero upstream PRs. The pinned revision is bumped deliberately (recompute the content id), never silently.
Development
pnpm install
pnpm dev # watch builds (worker + manifest bundles into dist/)
pnpm test # vitest: config validation, capability gating, lease paths
pnpm typecheck
pnpm buildpnpm install requires the packed Paperclip SDK snapshot in .paperclip-sdk/
(gitignored). Regenerate it with paperclipai plugin init --sdk-path against a
Paperclip checkout, or copy the tarballs from a prior dev home. plugin install
packages dist/, so run pnpm build BEFORE reinstalling into Paperclip.
Provenance
Developed in tandem with the daevix enclave work (bead bd_2_0-2ek) against a
short-lived local Paperclip integration branch, and validated end to end
against a live daevix dev deployment. The canonical source of this plugin AND
of the daevix runner image is now this repository; nothing daevix-related
remains in any Paperclip repo — the integration runs against stock Paperclip
with zero PRs.
