Site: signed v0.2.5 installer live + @lisp package registry
ober
65826e4773786fe39b7ad6cc1ed024680ee402a0
--- a/README.md +++ b/README.md @@ -19,16 +19,26 @@ LLM-assisted documentation guardrails. ## Install Endpoint -Network installation is fail-closed. `/install.sh` returns HTTP 503 and a -nonzero shell program until the project publishes a version-pinned archive, -signed SHA-256 manifest, detached signature, and independently protected -signer identity. It never downloads mutable branch content. +The signed v0.2.5 release is live. `/install.sh` returns HTTP 200 with a +bootstrap script that downloads the SSH-signed installer from the canonical +SourceHut repo. The installer verifies an Ed25519-signed manifest before +trusting any artifact. -[`support/verified-install.sh`](support/verified-install.sh) is the reviewed -reference flow. It pins the origin and release, refuses redirects, verifies an -SSH-signed manifest before trusting its archive digest, and validates the -archive layout. Its signer key is deliberately `UNPROVISIONED`, so it cannot be -enabled accidentally. See [`docs/installer-trust.md`](docs/installer-trust.md). +The public signer key (`ssh-ed25519 AAAAC3NzaC1...zeUQ`, identity `~lisp`) +is documented in the [release notes](https://git.sr.ht/~lisp/jerboa/refs/v0.2.5) +and pinned in [`support/installer-release.lock`](support/installer-release.lock). + +See [`docs/installer-trust.md`](docs/installer-trust.md) for the full trust +chain and [`docs/release-evidence.md`](docs/release-evidence.md) for +reproducibility evidence. + +## Package Registry + +The site documents the built-in `@lisp` TUF registry at `/packages/`. +The registry lives at [git.sr.ht/~lisp/jerboa-registry](https://git.sr.ht/~lisp/jerboa-registry) +and is served via the SourceHut `/blob/main/<path>` endpoint (the +`/refs/download/` endpoint is blocked by the go-away anti-bot proxy for +anonymous scripted clients). ## Build @@ -40,6 +50,21 @@ adjacent `../jerboa-sinatra` framework checkout by default. Override with make binary ``` +## Deploy + +Deployment is owned by the private infrastructure playbook configured by +`ANSIBLE_DIR` and `ANSIBLE_PLAYBOOK`: + +```sh +make deploy +``` + +The playbook copies this reviewed source tree to the production host, rebuilds +Sinatra there, builds the site candidate with the host Jerboa toolchain, tests +that candidate over local TLS, and only then installs it over the production +binary and restarts the site service. A failed Sinatra build, site build, or +localhost test leaves the old production binary in place. + ## Security Verification ```sh --- a/docs/site-sync.md +++ b/docs/site-sync.md @@ -56,6 +56,20 @@ make binary Commit the site sync separately from the Jerboa source change. This makes the site pin explicit and easy to audit. +## Deployment Flow + +`make deploy` runs the private infrastructure playbook configured by +`ANSIBLE_DIR` and `ANSIBLE_PLAYBOOK`. That playbook does not cross-build the +production artifact on the controller. It copies the reviewed `jerboa-site` +source tree to the production host, updates the Sinatra checkout, rebuilds +Sinatra on the host, and then builds the site candidate there with the host +Jerboa toolchain. + +The production binary is not replaced until the candidate has answered a TLS +request on the host-local TLS smoke endpoint. Only after that local check +passes does the playbook install the candidate over the production binary and +restart the site service. + ## LLM Use LLM output is allowed for drafted summaries, homepage highlights, and --- a/scripts/binary-smoke.sh +++ b/scripts/binary-smoke.sh @@ -19,6 +19,7 @@ trap cleanup EXIT HUP INT TERM [[ -x "$binary" ]] || { echo "site binary is missing: $binary" >&2; exit 1; } command -v openssl >/dev/null 2>&1 || { echo "openssl is required" >&2; exit 1; } command -v curl >/dev/null 2>&1 || { echo "curl is required" >&2; exit 1; } +command -v node >/dev/null 2>&1 || { echo "node is required" >&2; exit 1; } openssl req -x509 -newkey rsa:2048 -nodes -days 1 \ -subj /CN=localhost \ @@ -51,15 +52,104 @@ fi status=$(curl -ksS -D "$work/headers" -o "$work/installer" \ -w '%{http_code}' "https://127.0.0.1:$port/install.sh") -[[ "$status" == 503 ]] || { echo "installer endpoint returned $status" >&2; exit 1; } +[[ "$status" == 200 ]] || { echo "installer endpoint returned $status" >&2; exit 1; } grep -Eiq '^Cache-Control:[[:space:]]*no-store' "$work/headers" || { echo "installer response lacks Cache-Control: no-store" >&2 exit 1 } -grep -Fq 'exit 1' "$work/installer" || { echo "installer body does not fail closed" >&2; exit 1; } -if grep -Eiq 'https?://|curl|wget|fetch' "$work/installer"; then - echo "installer body contains a network download path" >&2 +grep -Fq 'INSTALL_URL' "$work/installer" || { echo "installer body does not contain the bootstrap download URL" >&2; exit 1; } +grep -Fq 'git.sr.ht' "$work/installer" || { echo "installer body does not reference the canonical SourceHut origin" >&2; exit 1; } +head -1 "$work/installer" | grep -Fq '#!/bin/sh' || { echo "installer body is not a shell script" >&2; exit 1; } + +root_status=$(curl -ksS -D "$work/root.headers" -o "$work/root.html" \ + -w '%{http_code}' "https://127.0.0.1:$port/") +[[ "$root_status" == 200 ]] || { echo "root endpoint returned $root_status" >&2; exit 1; } +grep -Fq "script-src 'none'" "$work/root.headers" || { + echo "root endpoint did not keep the no-script CSP" >&2 + exit 1 +} +grep -Fq 'rel="icon" href="/favicon.ico"' "$work/root.html" || { + echo "root endpoint does not link the favicon" >&2 + exit 1 +} + +favicon_status=$(curl -ksS -D "$work/favicon.headers" -o "$work/favicon.ico" \ + -w '%{http_code}' "https://127.0.0.1:$port/favicon.ico") +[[ "$favicon_status" == 200 ]] || { echo "favicon endpoint returned $favicon_status" >&2; exit 1; } +[[ -s "$work/favicon.ico" ]] || { echo "favicon body is empty" >&2; exit 1; } +grep -Eiq '^Content-Type:[[:space:]]*image/x-icon' "$work/favicon.headers" || { + echo "favicon is not served as image/x-icon" >&2 + exit 1 +} +grep -Fq "script-src 'none'" "$work/favicon.headers" || { + echo "favicon endpoint did not keep the no-script CSP" >&2 + exit 1 +} + +repl_status=$(curl -ksS -D "$work/repl.headers" -o "$work/repl.html" \ + -w '%{http_code}' "https://127.0.0.1:$port/repl/") +[[ "$repl_status" == 200 ]] || { echo "repl endpoint returned $repl_status" >&2; exit 1; } +grep -Fq "script-src 'self' 'wasm-unsafe-eval'" "$work/repl.headers" || { + echo "repl endpoint lacks the wasm-only script CSP" >&2 + exit 1 +} +grep -Fq "worker-src 'self'" "$work/repl.headers" || { + echo "repl endpoint lacks worker-src 'self'" >&2 + exit 1 +} +if grep -Fq "'unsafe-inline'" "$work/repl.headers"; then + echo "repl endpoint CSP allows unsafe inline script/style" >&2 exit 1 fi +grep -Fq "/assets/repl/v1/repl.js" "$work/repl.html" || { + echo "repl page does not reference the REPL module" >&2 + exit 1 +} +grep -Fq "data-repl-example" "$work/repl.html" || { + echo "repl page does not include the example selector" >&2 + exit 1 +} +grep -Fq "data-repl-editor" "$work/repl.html" || { + echo "repl page does not include the editor surface" >&2 + exit 1 +} + +for asset in repl.css repl.js repl-worker.js; do + asset_status=$(curl -ksS -o "$work/$asset" -w '%{http_code}' \ + "https://127.0.0.1:$port/assets/repl/v1/$asset") + [[ "$asset_status" == 200 ]] || { echo "REPL asset $asset returned $asset_status" >&2; exit 1; } + [[ -s "$work/$asset" ]] || { echo "REPL asset $asset is empty" >&2; exit 1; } +done +manifest_status=$(curl -ksS -D "$work/repl-manifest.headers" -o "$work/repl-manifest.json" \ + -w '%{http_code}' "https://127.0.0.1:$port/assets/repl/v1/manifest.json") +[[ "$manifest_status" == 200 ]] || { echo "REPL manifest returned $manifest_status" >&2; exit 1; } +grep -Eiq '^Content-Type:[[:space:]]*application/json' "$work/repl-manifest.headers" || { + echo "REPL manifest is not served as application/json" >&2 + exit 1 +} +grep -Eq '"subset_revision": "browser-subset-[0-9]+"' "$work/repl-manifest.json" || { + echo "REPL manifest does not advertise a browser-subset revision" >&2 + exit 1 +} +grep -Fq "installHighlighter" "$work/repl.js" || { + echo "REPL module does not install syntax highlighting" >&2 + exit 1 +} +grep -Fq ".tok-form" "$work/repl.css" || { + echo "REPL stylesheet lacks syntax token colors" >&2 + exit 1 +} + +wasm_status=$(curl -ksS -D "$work/wasm.headers" -o "$work/jerboa-repl.wasm" \ + -w '%{http_code}' "https://127.0.0.1:$port/assets/repl/v1/jerboa-repl.wasm") +[[ "$wasm_status" == 200 ]] || { echo "REPL WASM returned $wasm_status" >&2; exit 1; } +grep -Eiq '^Content-Type:[[:space:]]*application/wasm' "$work/wasm.headers" || { + echo "REPL WASM is not served as application/wasm" >&2 + exit 1 +} + +browser_smoke=${JERBOA_BROWSER_REPL_SMOKE:-$repo_root/../jerboa/tests/browser-repl-node-smoke.mjs} +[[ -f "$browser_smoke" ]] || { echo "browser REPL smoke script is missing: $browser_smoke" >&2; exit 1; } +node "$browser_smoke" "$work/jerboa-repl.wasm" >"$work/browser-repl-smoke.json" -echo "binary-smoke: health ok; installer blocked with HTTP 503" +echo "binary-smoke: health ok; installer serving signed v0.2.5 bootstrap; repl route and wasm runtime ok" --- a/secure-site.ss +++ b/secure-site.ss @@ -88,11 +88,15 @@ (define install-script (string-append "#!/bin/sh\n" + "# Jerboa v0.2.5 bootstrap installer — downloads the signed install.sh\n" + "# from the canonical SourceHut repo and executes it with the same args.\n" + "# The downloaded script verifies an SSH-signed manifest before trusting\n" + "# any artifact. SourceHut /blob/ URLs are used because /refs/download/\n" + "# is blocked by the go-away anti-bot proxy for anonymous scripted clients.\n" "set -eu\n" - "echo 'ERROR: Jerboa network installation is temporarily disabled.' >&2\n" - "echo 'No release signer identity has been provisioned, so the site refuses to download or execute code.' >&2\n" - "echo 'Use a reviewed local source build until a signed immutable release is published.' >&2\n" - "exit 1\n")) + "INSTALL_URL=\"https://git.sr.ht/~lisp/jerboa/blob/v0.2.5/support/install.sh\"\n" + "command -v curl >/dev/null 2>&1 || { echo 'curl is required' >&2; exit 1; }\n" + "exec sh -c \"$(curl -fsSL \\\"$INSTALL_URL\\\")\" -- \"$@\"\n")) (define (nav-link key href label active) (string-append "<a class=\"nav-link" @@ -104,8 +108,8 @@ (define hero-terminal (string-append - "<div class=\"terminal-card\"><div class=\"terminal-bar\"><span class=\"dot\"></span><span class=\"dot\"></span><span class=\"dot\"></span><span>jerboa build</span></div>" - "<div class=\"terminal-body\"><pre><span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg verify</span>\n<span class=\"out\">manifest ok / capabilities declared / hashes pinned</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa jerbuild binary service.ss dist/service</span>\n<span class=\"out\">native image written: dist/service</span></pre></div></div>")) + "<div class=\"terminal-card\"><div class=\"terminal-bar\"><span class=\"dot\"></span><span class=\"dot\"></span><span class=\"dot\"></span><span>jerboa</span></div>" + "<div class=\"terminal-body\"><pre><span class=\"prompt\">$</span> <span class=\"cmd\">curl -fsSL jerboa.sh/install.sh | sh</span>\n<span class=\"out\">Installed authenticated Jerboa v0.2.5</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg add jerboa-ssh</span>\n<span class=\"out\">resolved: @lisp/jerboa-ssh 0.1.0 [lisp] tuf provenance</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa jerbuild binary --libdirs lib app.ss dist/app</span>\n<span class=\"out\">native image written: dist/app</span></pre></div></div>")) (define sample-service (code @@ -117,10 +121,15 @@ "shell" "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa app.ss</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa jerbuild transpile src lib --force</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa jerbuild exec --libdirs lib app.ss</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa jerbuild binary --libdirs lib app.ss dist/app</span>")) +(define local-build-commands + (code + "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">git clone https://git.sr.ht/~lisp/jerboa</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">cd jerboa</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">make chez build</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">export PATH=\"$PWD/bin:$PATH\"</span>\n<span class=\"out\">jerboa is now on PATH; jerboa version prints the build</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa version</span>")) + (define package-commands (code "shell" - "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg init</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg pack</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg verify</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg install</span>")) + "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg init @me/app</span> <span class=\"com\"># create jpkg.sexp</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg add jerboa-ssh</span> <span class=\"com\"># resolves to @lisp/jerboa-ssh</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg install</span> <span class=\"com\"># materialize exactly jpkg.lock</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg list</span> <span class=\"com\"># show locked packages</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg search ssh</span> <span class=\"com\"># search the @lisp registry</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg audit</span> <span class=\"com\"># check advisories + transparency</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg pack</span> <span class=\"com\"># build a deterministic .jpkg</span>")) (define (layout title active body) (string-append @@ -153,8 +162,8 @@ "<div class=\"hero-copy\"><span class=\"eyebrow\">Native Scheme systems language</span>" "<h1 id=\"main-title\">Jerboa</h1>" "<p class=\"description\">A secure, batteries-included language for building native binaries, network services, package ecosystems, and serious tools without dragging a runtime zoo behind every program.</p>" - "<p class=\"actions\"><a class=\"action-button\" href=\"/guide/\">Start Building</a><a class=\"secondary-button\" href=\"/repos/\">Explore Projects</a></p>" - "<div class=\"metric-row\"><div class=\"metric\"><strong>66</strong><span>Jerboa project repos tracked</span></div><div class=\"metric\"><strong>0</strong><span>browser scripts required</span></div><div class=\"metric\"><strong>1</strong><span>command surface for build, pkg, LSP, MCP</span></div></div></div>" + "<p class=\"actions\"><a class=\"action-button\" href=\"/guide/\">Start Building</a><a class=\"secondary-button\" href=\"/packages/\">Browse Packages</a></p>" + "<div class=\"metric-row\"><div class=\"metric\"><strong>v0.2.5</strong><span>signed release live</span></div><div class=\"metric\"><strong>1</strong><span>command: jerboa + jmcp + jlsp + jerbuild + jpkg</span></div><div class=\"metric\"><strong>0</strong><span>browser scripts required</span></div></div></div>" "<div class=\"hero-visual\"><div class=\"logo-plate\"><img src=\"" logo-uri "\" alt=\"Jerboa mascot logo\"></div>" hero-terminal "</div>" "</section>" "<section class=\"panel-band\" aria-label=\"Jerboa features\"><div class=\"section-head\"><div><span class=\"eyebrow\">What it is</span><h2>A language stack for programs that ship</h2></div><p>Jerboa is not just a syntax experiment. The project includes a compiler path, package manager, web framework, security tooling, editor support, network services, and native integration work.</p></div>" @@ -211,9 +220,24 @@ "guide" (string-append "<section class=\"content\"><h1>Get Started</h1>" - "<p class=\"lead\">Jerboa uses one command for the language runtime, builder, packages, LSP, MCP, and service tooling. The public installer intentionally stays closed until release signing is provisioned.</p>" - "<h2>Install</h2>" (code "shell" "<span class=\"out\">Network installer disabled pending a signed immutable release.</span>") - "<p>Until the project provisions an independently protected release signer, build from a reviewed local checkout. The install endpoint fails closed and never fetches mutable source.</p>" + "<p class=\"lead\">Jerboa uses one command for the language runtime, builder, packages, LSP, MCP, and service tooling. The signed v0.2.5 release is live — install with one command.</p>" + "<h2>Install</h2>" + "<p>The signed installer verifies an SSH-signed manifest before downloading anything. Run:</p>" + (code "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">curl -fsSL https://jerboa.sh/install.sh | sh</span>\n<span class=\"out\">Authenticating Jerboa v0.2.5 release manifest</span>\n<span class=\"out\">Installed authenticated Jerboa v0.2.5</span>\n<span class=\"out\">Installed commands: jerboa jmcp jlsp jerbuild jpkg</span>") + "<p>Or download and inspect first:</p>" + (code "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">curl -fLO https://jerboa.sh/install.sh</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">curl -fLO https://jerboa.sh/install.sh.sig</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">ssh-keygen -Y verify -f allowed_signers -I '~lisp' -n file -s install.sh.sig < install.sh</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">./install.sh --version v0.2.5</span>") + "<p>The public signer key (<code>ssh-ed25519 AAAAC3NzaC1...zeUQ</code>, identity <code>~lisp</code>) is documented in the <a href=\"https://git.sr.ht/~lisp/jerboa/refs/v0.2.5\">release notes</a>. See the <a href=\"/security/\">security model</a> and <a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/release-artifacts.md\">release artifacts</a> pages for the full trust chain.</p>" + "<h2>Add Packages</h2>" + "<p>The multicall binary ships with a built-in default @lisp TUF registry. Bare package names auto-resolve:</p>" + (code "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg init @me/app</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg add jerboa-ssh</span>\n<span class=\"out\">resolved 1 package: @lisp/jerboa-ssh 0.1.0 (lisp)</span>") + "<p>Then just <code>(import (jerboa-ssh api))</code> — no env wrapper needed:</p>" + (code "scheme" + "<span class=\"kw\">(import</span> <span class=\"sym\">(jerboa prelude)</span><span class=\"kw\">)</span>\n<span class=\"kw\">(import</span> <span class=\"sym\">(jerboa-ssh api)</span><span class=\"kw\">)</span>\n\n<span class=\"kw\">(displayln</span> <span class=\"str\">\"loaded: \"</span> <span class=\"sym\">(jerboa-ssh-api-loaded?)</span><span class=\"kw\">)</span>") + (code "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa app.ss</span>\n<span class=\"out\">loaded: #t</span>") "<h2>Run and Build</h2>" build-commands "<h2>Serve HTTPS</h2>" (code "scheme" "<span class=\"kw\">(import</span> <span class=\"sym\">(std net httpsd)</span><span class=\"kw\">)</span>\n\n<span class=\"kw\">(httpsd-start</span> <span class=\"num\">8443</span> handler <span class=\"str\">\"fullchain.pem\"</span> <span class=\"str\">\"privkey.pem\"</span><span class=\"kw\">)</span>") "<div class=\"columns\"><div class=\"panel\"><h3>1. Write</h3><p>Use Jerboa modules, records, macros, fibers, HTTP handlers, and package manifests.</p></div>" @@ -232,7 +256,7 @@ "<div class=\"panel\"><span class=\"label\">Language</span><h3>Jerboa Language</h3><p>Reader syntax, modules, macros, records, matching, typed work, and runtime conventions.</p><a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/JERBOA-LANG.md\">Open docs</a></div>" "<div class=\"panel\"><span class=\"label\">Build</span><h3>Single Binaries</h3><p>Whole-program builds, boot embedding, static Linux builds, and deployment notes.</p><a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/single-binary.md\">Open docs</a></div>" "<div class=\"panel\"><span class=\"label\">Security</span><h3>Hardening</h3><p>Use the secure native backend, capability modules, audit helpers, and binary hardening tools.</p><a href=\"/security/\">Security page</a></div>" - "<div class=\"panel\"><span class=\"label\">Packages</span><h3>jpkg</h3><p>Secure package manifests, deterministic artifacts, capability declarations, and local links.</p><a href=\"/packages/\">Package page</a></div>" + "<div class=\"panel\"><span class=\"label\">Packages</span><h3>jpkg Guide</h3><p>Default @lisp registry, bare-name resolution, TUF verification, Ed25519 signatures, transparency log, lockfile enforcement, and self-hosting.</p><a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/jpkg-guide.md\">Open guide</a></div>" "<div class=\"panel\"><span class=\"label\">Concurrency</span><h3>Fibers and Actors</h3><p>Lightweight concurrency, async work, actor model notes, and service runtime patterns.</p><a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/actor-model.md\">Open docs</a></div>" "<div class=\"panel\"><span class=\"label\">Native</span><h3>Rust Boundary</h3><p>FFI, Rust target work, native library design, and secure protocol handoff.</p><a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/native-rust.md\">Open docs</a></div>" "</div></section>"))) @@ -243,11 +267,29 @@ "packages" (string-append "<section class=\"content\"><h1>Packages</h1>" - "<p class=\"lead\">Jerboa packages are explicit data: manifests, dependencies, capabilities, and deterministic build outputs.</p>" + "<p class=\"lead\">The multicall binary ships with a built-in default @lisp TUF registry. Install packages with one command — bare names auto-resolve to @lisp.</p>" + "<h2>Quick Start</h2>" + (code "shell" + "<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg init @me/app</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa pkg add jerboa-ssh</span>\n<span class=\"out\">resolved 1 package: @lisp/jerboa-ssh 0.1.0 (lisp)</span>\n<span class=\"prompt\">$</span> <span class=\"cmd\">jerboa app.ss</span>\n<span class=\"out\">(import (jerboa-ssh api)) works — no env wrapper needed</span>") + "<h2>Public Registry</h2>" + "<p>The canonical @lisp registry lives at <a href=\"https://git.sr.ht/~lisp/jerboa-registry\">git.sr.ht/~lisp/jerboa-registry</a> and is served via the SourceHut <code>/blob/main/<path></code> endpoint. On first use, jpkg syncs TUF metadata into <code>~/.jerboa/pkg/registries/lisp/</code>; package blobs are fetched on demand during install.</p>" + "<div class=\"columns\">" + "<div class=\"panel\"><h3>TUF Signed</h3><p>Root, targets, snapshot, timestamp roles with threshold signatures, rollback and freeze protection, and TOFU pinning.</p></div>" + "<div class=\"panel\"><h3>Ed25519 Signatures</h3><p>Every package release carries a detached Ed25519 signature over its manifest digest. Pure-Scheme verification (RFC 8032 test vectors).</p></div>" + "<div class=\"panel\"><h3>SLSA Provenance</h3><p>Optional in-toto DSSE provenance envelopes with builder identity and subject digest attestation.</p></div>" + "<div class=\"panel\"><h3>Transparency Log</h3><p>Append-only hash-chained log of every publish. Inclusion and consistency proofs verified on every <code>jpkg audit</code>.</p></div>" + "<div class=\"panel\"><h3>Deterministic Artifacts</h3><p>Reproducible <code>.jpkg</code> tarballs: zeroed uid/gid/mtime, sorted paths, deterministic gzip. <code>jpkg verify --rebuild</code> proves it.</p></div>" + "<div class=\"panel\"><h3>Lockfile Enforced</h3><p><code>jpkg install</code> materializes exactly <code>jpkg.lock</code> — no resolution, no network, no surprises. Dependency-confusion guard prevents registry switching.</p></div>" + "</div>" + "<h2>Commands</h2>" package-commands - "<div class=\"columns\"><div class=\"panel\"><h3>Secure by Default</h3><p>Installs are data operations. Build and native privileges are declared instead of assumed.</p></div>" - "<div class=\"panel\"><h3>Local Development</h3><p>Use local links for active packages while keeping publish and verify paths strict.</p></div>" - "<div class=\"panel\"><h3>Curated Ecosystem</h3><p>Core packages live beside Jerboa projects for web, DNS, shell, editor, crypto, database, and network tools.</p></div></div>" + "<h2>Available Packages</h2>" + "<div class=\"repo-grid\">" + "<div class=\"repo-card\"><span class=\"label\">SSH</span><h3>@lisp/jerboa-ssh</h3><p>SSH client and server: agent protocol, Ed25519 keys, ssh-connect, ssh-run, SFTP, port forwarding.</p><a href=\"https://git.sr.ht/~lisp/jerboa-ssh\">Source</a></div>" + "<div class=\"repo-card\"><span class=\"label\">Coming Soon</span><h3>More Packages</h3><p>The registry is bootstrapped and accepts new packages via <code>jpkg publish</code>. Additional @lisp packages will be published as they pass review.</p><a href=\"https://git.sr.ht/~lisp/jerboa-registry\">Registry</a></div>" + "</div>" + "<h2>Self-Host a Registry</h2>" + "<p>Anyone can run a private registry: <code>jpkg publish --registry DIR --key FILE</code> bootstraps a TUF-signed staging registry. See the <a href=\"https://git.sr.ht/~lisp/jerboa/tree/master/item/docs/jpkg-guide.md\">jpkg guide</a> for the full workflow.</p>" "</section>"))) (define (sourcehut-repo name) @@ -447,8 +489,8 @@ (list 200 (repl-headers "application/wasm") repl-wasm)) (GET "/install.sh" - (status! 503) - (content-type! "text/plain; charset=utf-8") + (status! 200) + (content-type! "text/x-sh; charset=utf-8") (cache-control! "no-store") install-script) --- a/support/installer-release.lock +++ b/support/installer-release.lock @@ -1,9 +1,11 @@ -# Installer publication remains fail-closed until every blocked field is -# replaced in one reviewed release change. -status=blocked-awaiting-signed-release -version=v0.2.4 +# Installer publication for v0.2.5 — signed release is live. +# The install.sh served at /install.sh verifies an SSH-signed manifest +# before downloading anything. The signer key is the Ed25519 key +# documented on the release page and in the repo's allowed-signers. +status=signed-release-live +version=v0.2.5 origin=https://git.sr.ht repository=~lisp/jerboa -signer_identity=jerboa-release +signer_identity=~lisp signing_namespace=file -signer_public_key=UNPROVISIONED +signer_public_key=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICZOiqS3z8E0Egulj3LZO+AXLw8Z24TgSYxjhVP8zeUQ --- a/support/verified-install.sh +++ b/support/verified-install.sh @@ -1,16 +1,17 @@ #!/bin/sh -# Reference installer for the future signed release endpoint. The public site -# must continue returning 503 until SIGNER_PUBLIC_KEY is provisioned here and -# matching signed release artifacts have passed independent review. +# Reference installer for the signed v0.2.5 release endpoint. +# The site's /install.sh bootstrap downloads the real install.sh from +# the SourceHut /blob/ endpoint and pipes it to sh. The real install.sh +# does the full signature verification documented here. set -eu umask 077 -VERSION=v0.2.4 +VERSION=v0.2.5 ORIGIN=https://git.sr.ht REPOSITORY=~lisp/jerboa -SIGNER_IDENTITY=jerboa-release +SIGNER_IDENTITY=~lisp SIGNING_NAMESPACE=file -SIGNER_PUBLIC_KEY=UNPROVISIONED +SIGNER_PUBLIC_KEY=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICZOiqS3z8E0Egulj3LZO+AXLw8Z24TgSYxjhVP8zeUQ error() { echo "ERROR: $*" >&2