Start a local validation service safely
July 18, 2026 ยท View on GitHub
When your Sequence depends on a local file or another startup resource, validate it before you expose a service route. This guide packages a Sequence, runs it on an installed local Hub, and shows the difference between a usable instance and one that fails during startup. It is motivated by the public Server Fault question about diagnosing an unreachable server.
For lifecycle details, read sequence lifecycle and readiness.
Prerequisites
- Node.js 18 or later and npm
- The Scramjet CLI (
si) and Transform Hub CLI (sth) installed and available on yourPATH - A Sequence project that can be built with
npm run build - A file that the Process Adapter runner can read, or a plan to package that file with your Sequence
curl,timeout, andshfor the readiness command below
This example uses the Process Adapter. You can run the same Sequence with Docker or Kubernetes, but you need to account for their filesystem visibility and startup-timeout settings. Treat dataFile as a packaged or explicitly mounted resource, not as an arbitrary host path.
Validation-first sequence
import { access } from "node:fs/promises";
import type { SequenceAppContext } from "@scramjet/sequence-types";
export async function initialize(this: SequenceAppContext) {
const file = this.config.dataFile as string;
try {
await access(file);
} catch (error) {
this.logger.error("validation failed", { code: "RESOURCE_UNAVAILABLE", error: String(error) });
this.destroy(new this.AppError("GENERAL_ERROR", String(error)));
throw error;
}
this.api.use("/status", () => ({ ready: true, instanceId: this.instanceId }));
this.logger.info("validation passed");
}
export default async function (this: SequenceAppContext) {
await new Promise(() => {});
}
Route registration follows validation. Until access() succeeds, no exposed listener is active. If validation fails, the Sequence writes a structured error, destroys its instance, and rethrows the original error. The instance becomes errored; start a new instance after you fix its configuration or resource instead of retrying inside the failed one. The pending promise represents the long-running phase after readiness, not a retry mechanism.
Your trust boundary is the packaged resource, configuration, and adapter mount. Validate paths and permissions without returning file contents. The Sequence does not manage Hub authentication, public ingress, or secret storage. A dropped client connection does not replay earlier requests.
Package and run with the Process Adapter
Follow the installed Sequence setup and run guide for the complete package contract. In your Sequence project, install dependencies, build, install production dependencies, and create the archive:
Set a non-empty exposePath in the sequence package metadata to start the sequence API server. For example, add "exposePath": "/validation-status" to package.json; the supported instance RPC request below identifies the instance explicitly and forwards path: "/status" to its API.
Packaging terminal
npm install
npm run build
npm install --production
si sequence pack . -o validation-service.tar.gz
Hub terminal
mkdir -p sequence-store
sth --runtime-adapter process --hostname 127.0.0.1 --port 8000 --sequences-root "$PWD/sequence-store"
Readiness terminal
timeout 60s sh -c '
until curl --fail --silent http://127.0.0.1:8000/api/v1/status |
node -e "let s=\"\"; process.stdin.on(\"data\", c => s += c).on(\"end\", () => process.exit(JSON.parse(s).ready === true ? 0 : 1))";
do :; done
'
Point si at the running Hub and deploy with the configuration key used by this example. dataFile must resolve inside the runner's packaged or explicitly mounted filesystem:
Deploy/start terminal
si config set apiUrl http://127.0.0.1:8000
si sequence deploy ./validation-service.tar.gz --config-string '{"dataFile":"/path/visible-to-the-runner/data.json"}'
# Or separate upload and start when those operations must be split:
si sequence send ./validation-service.tar.gz
si sequence start <sequence-id> --config-string '{"dataFile":"/path/visible-to-the-runner/data.json"}'
si instance list
si instance info <instance-id>
si instance log <instance-id>
Instance status terminal
Request the sequence route through the supported v2 instance RPC operation. The outer request is always POST; its JSON metadata contains the sequence request method, path, and headers:
curl --fail --request POST \
"http://127.0.0.1:8000/api/v2/instances/<instance-id>/rpc/status" \
-H 'content-type: application/json' \
--data '{"method":"GET","path":"/status","headers":{"accept":"application/json"}}'
With the Process Adapter, the Hub owns the child process lifecycle and stops it when the Hub stops; it does not provide container filesystem or resource isolation. If you deploy through a Manager, connect the Hub to that Manager first and set apiUrl to the Manager endpoint. The Manager routes the deployment, while the connected Hub's Process Adapter runs the Runner.
Local verification (optional)
Confirm that the Hub reports ready: true, then inspect the instance and its logs. The RPC response contains status, headers, and body; after validation passed appears, its body is the sequence response with ready: true. A missing or inaccessible dataFile instead logs RESOURCE_UNAVAILABLE and leaves the instance errored without an active route.
What this demonstrates
Validation before route registration prevents a Sequence with a missing or inaccessible dataFile from exposing /status as though it were usable. The explicit instance RPC route lets you request the ready status only from the intended instance after validation succeeds. A successful run shows the instance ready, its log confirms validation passed, and the RPC response contains the bounded status.