Validate a checkout before deployment
Set PROJECT to the absolute path of the checkout you intend to validate. Read its module code and generated skill first. Do not validate an untrusted checkout as though loading were inert.
rune steel fmt --check --repo "$PROJECT"
rune steel check --repo "$PROJECT"
rune steel test --repo "$PROJECT"
rune steel docs generate --repo "$PROJECT"
rune steel docs show --repo "$PROJECT"
rune steel docs stdlib --repo "$PROJECT"If formatting reports dirty files, run rune steel fmt --repo "$PROJECT", review the diff, and repeat the failed check. The formatter can skip modules/main.scm when a rewrite would change extracted ;;! documentation. Review that file explicitly.
To test a declared command before deployment, use rune exe --repo "$PROJECT" COMMAND with the command's real arguments. Inspect its structured result and artifacts. After a successful main push, use rune exe NAME COMMAND to exercise the active deployment. Those forms do not select the same source.
Expose a local HTTP route
Use an inspected service module that declares service/define-service, route handlers, and lifecycle readiness. If supplied with your authorized project, the hello-json example's service file is modules/hello-json-service.scm. The rpn example uses modules/rpn-service.scm. Read the matching implementations, tests, and READMEs. If those files are absent, do not assume a public binary archive supplies them. A CLI-only deployment never opens an HTTP listener.
- Copy the chosen service implementation into the checkout as
modules/main.scm. Adapt the tests, identity, and README to its intended contract. - Validate the checkout with the commands above.
- In the named home's
runtime.toml, choose an unused loopback port. The example below uses 8080.
[network.service_http]
handler_workers = 1
[network.service_http.endpoint]
kind = "tcp"
address = "127.0.0.1:8080"- Start the named Rune to serve Git SSH, if it is stopped. Push the validated checkout to main.
- If the process was already running when you changed config, stop it gracefully and start it again. Config edits are not live reloads.
- Read
rune status NAMEfor the actual endpoint, deployment, and process instance before sending a request.
For the deployed hello-json service, the source contract is POST /api/hello returning JSON with status: "ok" and message: "hello". It also declares GET /healthz, GET /readyz, and GET /assets. For the full RPN service, POST /v1/eval with {"expression":"2 3 +"} has a result of 5. Invalid tokens, stack underflow, division by zero, and overflow have structured errors. These are example-specific routes, not automatic Rune endpoints.
Use HTTP limits when choosing worker count. Loopback binding does not configure a public proxy, TLS, client authentication, or access policy.
Store application data durably
Use a logical database name and disk mode for data that must survive a process restart. Run this inspected example within your Scheme program or a disposable REPL project. Each call returns a result that your program must check before continuing.
(sqlite/open "state" "disk")
(sqlite/execute "state"
"create table if not exists counter (value integer)" "[]")
(sqlite/execute "state" "insert into counter values (?1)"
"[{\"type\":\"integer\",\"value\":1}]")
(sqlite/query-one "state" "select value from counter limit 1" "[]")
(sqlite/close "state")A query-one success contains typed cells under result.row. Do not treat the full result object as a row or a handle. Use sqlite/begin with read or write, extract the transaction token from result, then use sqlite/tx-query or sqlite/tx-execute and commit or roll back. Unfinished invocation-scoped transactions roll back on completion, errors, or interruption.
Named disk databases live beneath the home's private data/sqlite/. Repo execution keeps them beneath the checkout's .rune/data/sqlite/. Use memory only when process lifetime is the intended lifetime. Never point an app at state/rune.sqlite3, Rune's control database. Paths are not valid database names. Back up valuable data with an operator-reviewed SQLite-consistent method.
Generate a service definition for review
Choose the init system for the host. These commands print definitions to stdout and do not install, enable, or start a service:
rune --home "$HOME/.rune" service generate hello --system systemd
rune --home "$HOME/.rune" service generate hello --system launchd
rune --home "$HOME/.rune" service generate hello --system openrcInspect the absolute binary, home, account, working directory, and start invocation. The optional --output PATH writes a definition atomically. Installation, log collection, restart policy, and account permissions belong to the operator's init-system setup. Do not start a second foreground process for the same home.
Diagnose a failed start, push, or request
- Capture
rune --version, the relevant subcommand help, andrune --home ROOT --json status NAME. Confirm the same absolute root and name in each terminal. - If the home is not initialized, inspect your root selection. If the process is stopped, start it explicitly. If another process holds the runtime lock, find its owning terminal or supervisor rather than deleting the lock.
- For a rejected push, read the validation or activation error. Re-run the failing check in the intended checkout. Do not edit deployment files or the control database to bypass rejection.
- For a missing endpoint, confirm that the active bundle declares HTTP, the config permits loopback TCP or a Unix socket under
run/, and the current process loaded that config. - For a 404, compare the exact case-sensitive method and path with the deployed route declarations. Health paths are optional. A registry annotation alone is not a callable route.
- For
storage_unavailable, inspect the named home's ownership, private permissions, write access, anddata/sqlite/. Preserve the data and repair the directory. Do not substitute a control-state path. - For
invalid_path, keep repo paths relative and inside the selected repo. For a missing primitive or module, consult this build's stdlib reference instead of installing arbitrary Steel cogs.
Read error envelopes at the correct layer. Before sharing diagnostics, remove private filesystem paths, keys, credentials, request bodies, and personal application data.
Inspect the maintainer capsule boundary
Forge is a Linux maintainer preview, not a public release installer. Continue only with a compatible existing Rune checkout, an installed Rust/Cargo toolchain, and all locked dependencies available offline. It does not install a toolchain or fetch missing dependencies.
After preparing those prerequisites, inspect rune forge --help and rune init --help. The source contract uses explicit project and toolchain paths:
rune forge --repo "$PROJECT" --target capsule --output "$CAPSULE" \
--rune-checkout "$RUNE_CHECKOUT"
rune init --import "$CAPSULE" "$DESTINATION"Set CAPSULE and DESTINATION to unused paths. Forge and import refuse overwrite or merge. Import validates the capsule before staging. Import does not execute Scheme, resolve credentials, create a managed home, or run the project. The imported directory is a source project. Do not pass a capsule to a supposed run command.
Capsules carry a CycloneDX 1.6 SBOM and unsigned provenance. Checksums and provenance support integrity and reproducibility, not publisher authentication. The named binary image target is blocked by the compiled-image API's inability to preserve Rune primitive registration. binary, oci, and unikernel return unsupported_target and create no artifact. A Flint workflow's OCI roots do not make the Forge OCI output target supported.