Install a customer-hosted server
Verify the offline release, install the core server on one Linux machine, create the first administrator, enroll an endpoint and ask it a question.
What this installs
The package installs the console, the Query API, the MCP service, the relay, the application database and a local TLS proxy on one dedicated Linux x86-64 server. Endpoints are installed separately from their own signed artifact. Retention, compute and inference roles are absent, and none is needed to install or start the server.
1. Prepare the host
- A dedicated Linux x86-64 virtual machine. See Deployment profiles for the starting footprint.
- The operating system, service manager, application database, TLS proxy and bootstrap runtime at the exact package versions pinned in the signed release, installed from verified offline platform media. The installer checks every pinned version and never downloads a package.
- The application database listening only on localhost, with local administrator access for the operating system's database account.
- No other web server on TCP 443.
- A local DNS entry or host mapping for your console name, for example recursift.customer.example, on every client and endpoint, and accurate local time. No public DNS or external time service is needed.
- A server certificate for that name from your certificate authority, or from a private certificate authority whose private key stays on a protected administration machine, never on the server. Put server.crt (with any intermediates), server.key and ca.crt in one directory.
2. Stage the release and keys
Copy the four signed artifact sets to a staging directory that only root or the operator can write. Keep every file in each set. The console set pins your public HTTPS origin, because the origin is a build input.
media/
webapp/ manifest.json, manifest.sig, composition.json, console bundle, runtime, SBOMs, notices
api/ manifest.json, manifest.sig, API, MCP and admin binaries, migration, SBOMs, notices
relay/ manifest.json, manifest.sig, relay binaries, SBOMs, notices
agent/ manifest.json, manifest.sig, endpoint executable, install contract, SBOM, noticesPut webapp.pub, api.pub, relay.pub and agent.pub in a separate keys directory. Obtain them from Recursift's authenticated release records and compare their fingerprints out of band. A key that arrives only beside the artifacts is not a trust root. Never put a private signing key on the server.
3. Verify without changing the host
Run the read-only check first. It needs neither root nor a network connection. Run it with the installer tools and runtime from independently trusted media; never run a downloaded verifier to judge that same download.
bash deploy/customer/install.sh --dry-run --artifacts /media/recursift \
--keys /trusted/recursift-keysSuccess prints Verified four artifact sets, the composition hash and No optional roles, then Dry run: signatures, hashes, versions, required files and targets verified. No host changes. The check fails on a bad signature, an extra, missing or altered file, a symbolic link, a version other than the one pinned in the composition, a binary for a target other than Linux x86-64, or any optional role.
4. Install
sudo bash deploy/customer/install.sh --artifacts /media/recursift \
--keys /trusted/recursift-keys --origin https://recursift.customer.example \
--tls /secure/recursift-tlsIn order, the installer:
- Verifies all four artifact sets and confirms that --origin matches the origin signed into the release.
- Confirms Linux x86-64, the required system commands, and the exact platform package and runtime versions.
- Refuses to continue if any Recursift state, application directory, database or database role already exists, or if the database listens beyond localhost.
- Checks that the certificate chains to ca.crt, matches the console name, and matches server.key.
- Generates the secrets, freezes a root-owned copy of the release and verifies that copy again before running anything from it.
- Confirms the local sign-in bootstrap is present, installs the service binaries, creates the database and its roles, applies the fresh schema baseline and the signed API migration, and records them in a schema ledger.
- Creates one service account per service with no login shell, installs the service units, writes the proxy configuration for the validated console name, and maps that name to loopback on the server.
- Starts the services, waits up to 60 seconds for each health check, checks the console through TLS, then records the installed release.
Success ends with Core server services are healthy. Identity and endpoint enrollment remain explicit operator steps. Running the installer again with the same release prints This exact release is already installed. No schema changes. A failed install stops the services it started and keeps its partial state for diagnosis; do not run it again over that state.
Generated secrets
Secrets are generated with cryptographic randomness into mode 0600 files under /etc/recursift/customer: the console sign-in secret, the relay shared secret, the settings encryption key, the configuration signing key, and separate console and API database passwords. Each service reads only its own file. roles.sql holds the database role passwords for restore only.
Ports and accounts
| Service | Listener | Account |
|---|---|---|
| Local TLS proxy | TCP 443, the only published port | recursift-proxy, with only the capability to bind port 443 |
| Console | Loopback TCP 8080 | recursift-webapp; reads and writes application tables, no schema changes |
| Query API | TCP 8081, localhost traffic only | recursift-api; restricted query database role |
| MCP | TCP 8082, localhost traffic only | recursift-mcp; no database credential |
| Relay | TCP 8083, localhost traffic only | recursift-relay; shared relay secret, no storage |
| Application database | Loopback TCP 5432 | The operating system's database account |
The proxy sends / to the console, /v1/ to the Query API, /mcp to MCP, and /ws to the relay. The four application services may exchange traffic only over loopback; confirm that restriction is active in the service logs and with an outbound-denial test. Configure the server firewall to allow TCP 443 and your management access only.
5. Check health
systemctl status recursift-{webapp,api,mcp,relay,proxy}
curl --fail http://127.0.0.1:8080/api/health
curl --fail http://127.0.0.1:8081/health
curl --fail http://127.0.0.1:8082/health
curl --fail http://127.0.0.1:8083/health
curl --fail --cacert /etc/recursift/customer/tls/ca.crt \
https://recursift.customer.example/api/healthThe console and API checks include a database ping. The MCP and relay checks show only that the listener is up. Healthy listeners do not show that sign-in, enrollment or a query works; complete the steps below before calling the server ready.
6. Create the first administrator
sudo bash deploy/customer/bootstrap-admin.sh --email admin@customer.exampleThis prints a one-time setup link. Open it privately in a browser that trusts your certificate authority and choose a password. Customer-hosted sign-in and recovery explains expiry, recovery and lockout.
7. Enroll an endpoint
Import ca.crt into the endpoint's trust store. In the local console, use Add agent to get a one-time enrollment token. In the customer profile, the enrollment command the console shows points at your own origin, and the relay address given to endpoints is wss:// followed by your console name and /ws. Do not use a public download link.
8. Issue an API key
API and MCP access need a customer mapping to the console owner and a scoped key, created with the verified admin utility at /opt/recursift/customer/recursift-admin. Give it the database administrator connection in ADMIN_DATABASE_URL for that one process only; never put it in a service's environment. The key is printed once.
/opt/recursift/customer/recursift-admin -operation customer \
-owner-user-id EXISTING_USER_ID -name 'Customer name'
/opt/recursift/customer/recursift-admin -operation key \
-customer-id CUSTOMER_ID -name 'Local MCP' \
-scopes agents:read,query:read,query:write -days 909. Ask a local question
The Query API and MCP service answer on your own origin: /v1 for the API and /mcp for MCP. Requests and responses follow the same contract as the hosted API; only the base address changes. Pass --cacert or install ca.crt so certificate verification stays on.
export RECURSIFT_BASE="https://recursift.customer.example/v1"
export RECURSIFT_CA="/path/to/ca.crt"
curl --fail-with-body --cacert "$RECURSIFT_CA" "$RECURSIFT_BASE/me" \
-H "Authorization: Bearer $RECURSIFT_API_KEY"
curl --fail-with-body --cacert "$RECURSIFT_CA" "$RECURSIFT_BASE/agents?limit=20" \
-H "Authorization: Bearer $RECURSIFT_API_KEY"
curl --fail-with-body --cacert "$RECURSIFT_CA" "$RECURSIFT_BASE/queries" \
-H "Authorization: Bearer $RECURSIFT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
--data "$(jq -n --arg id "$RECURSIFT_AGENT_ID" '{
question: "What operating system are you running?",
target: {agent_ids: [$id]},
timeout_seconds: 120
}')"
curl --fail-with-body --cacert "$RECURSIFT_CA" "$RECURSIFT_BASE/queries/$RECURSIFT_QUERY_ID" \
-H "Authorization: Bearer $RECURSIFT_API_KEY"Checked against OpenAPI 0.1.0 at api 92e785d: GET /me, GET /agents with limit, POST /queries with question, target.agent_ids and timeout_seconds and an optional Idempotency-Key header, and GET /queries/{id}. The POST returns 202 with a receipt; poll the query until it is complete, partial, timed_out or cancelled, as in Your first endpoint query.
What the customer profile turns off
| Dependency | Customer profile |
|---|---|
| Hosted sign-in and email delivery | Not registered. Local sign-in only. |
| Web fonts from a remote host | Omitted; system fonts are used. |
| Remote model catalogs, completions and connection tests | Routes return 503; outbound calls are refused. |
| Vulnerability feed and discovery | Not scheduled and no feed token is set. Supply approved offline data separately before claiming current coverage. |
| Remote key management for stored provider secrets | Unset; the generated local settings key is used. |
| Public download buttons and hosted legal links | Navigation only, not startup requests. Use verified offline media and your own legal material. |
| Package registries | Build-side only. The installer resolves no dependencies. |
| DNS, time and certificates | Your own names, clock and certificate authority. |
In the customer profile the console refuses outbound requests to anything other than its own origin and loopback, and answers optional inference, model-catalog and vulnerability-feed routes with 503 Optional services are unavailable in the customer core profile. The service manager also limits the application services to loopback traffic. The proxy does not write access logs.
Optional container composition
The package also includes a container composition that publishes only TCP 443, pulls nothing at startup and requires a digest for every image. It is an optional path, not the primary installer, and the backup and removal scripts do not target it.