Operation
This section is for operators running Synchronic Web infrastructure in real environments.
It covers deployment, service roles, network API behavior, routine operations, and protocol specifications.
Unless otherwise noted, examples target the deploy/compose/general stack.
Deployment
Section titled “Deployment”This section describes how to bring up and manage the reference runtime stack used throughout the rest of this guide.
Compose deployment
Section titled “Compose deployment”The Compose deployment is the baseline operating mode for local, staging, and many production-like environments.
It works with Compose-compatible runtimes such as Docker Compose, Podman Compose, and podman-compose.
It provides a predictable process model so operators can troubleshoot behavior without guessing which component wiring changed.
Required/important environment variables:
COMPOSE_PROJECT_NAME(recommended): stack identity; isolates generated volumes and prefixes container/network namesSECRET(required): Root secret supplied only to JournalINTERFACE_SECRET(required): independently rotatable Interface bearer supplied to Journal and Gateway and persisted in private Root state; it must differ fromSECRETthroughout installation and rotationINTERFACE_ADMINS(defaultadmin): comma-separated local usernames installed as(*state* <name>)administrators on a fresh databaseADMIN_USERNAME/ADMIN_PASSWORD: independent identity-provider bootstrap accountHTTP_PORT(default8192): published HTTP portHTTPS_PORT(default8193): published HTTPS portPERIOD(default8): stepping cadence parameter passed to journalWINDOW(default1024): retention window for unpinned historyTLS_CERT_HOST_PATH(optional): host certificate file mounted into routerTLS_KEY_HOST_PATH(optional): host key file mounted into router
Start:
COMPOSE_PROJECT_NAME=sync-local SECRET=root-password \INTERFACE_SECRET=interface-password ADMIN_PASSWORD=admin-pass \tests/api/local-compose.sh upSmoke validation:
COMPOSE_PROJECT_NAME=sync-local tests/api/local-compose.sh smokeDirect local HTTP deployment (no TLS):
COMPOSE_PROJECT_NAME=sync-dev SECRET=root-password \INTERFACE_SECRET=interface-password ADMIN_PASSWORD=admin-pass \HTTP_PORT=8192 HTTPS_PORT=8193 \docker compose -f deploy/compose/general/compose.yaml up -dOptional public TLS deployment with the same compose file:
COMPOSE_PROJECT_NAME=sync-prod \TLS_CERT_HOST_PATH=/absolute/path/to/fullchain.pem \TLS_KEY_HOST_PATH=/absolute/path/to/privkey.pem \SECRET=root-password INTERFACE_SECRET=interface-password \ADMIN_PASSWORD='<independent-password>' ORIGIN=https://example.com HTTP_PORT=80 HTTPS_PORT=443 \docker compose -f deploy/compose/general/compose.yaml up -dStop without deleting data:
COMPOSE_PROJECT_NAME=sync-dev docker compose -f deploy/compose/general/compose.yaml downOnly add -v when you intentionally want to delete that stack’s database and identity-provider volumes.
The default stack includes:
- journal runtime (
journal-sdkplus mounted general Lisp deployment inputs) - router/nginx entrypoint
- gateway API service
- explorer UI
- workbench UI
- file-system WebDAV service
The compose stack does not use a separate immutable general image. Instead,
journal-sdk starts through deploy/compose/general/run.sh, which reads mounted
records/lisp/*.scm files. Final Sync Web 1.6 supports one transition from a
version-marked 1.5.0 database when JOURNAL_UPDATE=1. The minimal nonfresh
Interface path preserves required history, derived keys, relationships, and
authorization meaning; incompatible required state fails atomically. Compose
startup accepts only the exact successful installer result. A process failure,
Scheme error value, or malformed result exits nonzero before writing or advancing
the version marker or launching the server, and result/credential content is
omitted from diagnostics. With JOURNAL_UPDATE=0, a correctly version-marked
database reopens normally without reinstalling or interpreting mounted record
source during startup.
The 1.5 Tree-native value layout removes the released Document wrapper and all record-code migration machinery. Do not apply 1.5 in place to a supported 1.4.x database or an earlier 1.5 development database.
Root remains host-only and is never supplied to Gateway or persisted. The independently rotatable Interface bearer is private Root state so trusted periodic orchestration can reenter the ordinary Interface. It never enters Ledger/Federation objects, user state, proofs, traces, or public configuration. Treat Journal backups as live Interface credentials and rotate after disclosure or untrusted restoration.
Transitioning from 1.4.x
Section titled “Transitioning from 1.4.x”Before starting 1.5, stop the old journal cleanly and preserve:
- a consistent copy of the RocksDB database volume;
- the exact 1.4.x journal image or binary;
- deployment configuration and the secrets needed to authenticate historical reads; and
- a manifest recording the image digest, platform version, chain size, and backup checksum.
Restore that archive in isolation and verify representative historical reads,
including index 0 and (*state* *time*), before changing the production
service. Keep the archive read-only: do not run periodic steps, writes, or bridge
synchronization against it. Start 1.5 with a new database volume, recreate local
policy and reciprocal bridges, and import only application values selected for
the new history. A logical import does not preserve old Merkle roots, signatures,
pins, or cryptographic lineage.
Routing
Section titled “Routing”The service split allows teams to scale or replace one layer at a time. For example, router behavior can be adjusted independently from journal runtime updates. The router service is the public nginx entry point for the deployment. It serves runtime query routes and UI routes:
| Route | Purpose |
|---|---|
/interface | Scheme or JSON request endpoint selected by request Content-Type. |
/docs | Gateway Swagger/OpenAPI documentation and route orientation. |
/api/v1/... | Versioned gateway API endpoints proxied to gateway service. |
/explorer/ | Browser UI with Ledger, Stage, Self-local Access, and admin-only Self-local Admin modes. |
/workbench/ | Developer-oriented query workspace with API aids. |
/webdav/ | WebDAV file-system projection: /stage (mutable), /ledger/state (current committed, index optional), /control/pin (pin directives). |
Router mode is selected at startup:
- HTTP mode when TLS cert/key files are not found.
- TLS mode when both cert/key files are present.
Network API
Section titled “Network API”Implemented by records/lisp/interface.scm on top of root.scm, standard.scm, and ledger.scm.
Permission boundaries are central to safe operation, so operators should treat function families as security domains.
Function Families
Section titled “Function Families”| Family | Description | Functions |
|---|---|---|
| Public bridge/control/proof | Direct protocol operations with no signed application invocation. Outward Gateway $federation context is rejected. | size, info, route, synchronize!, constrained trace, trace-batch |
| Authenticated application | Local calls use session/API-token authentication; signed federation is limited to scalar staged access, dedicated staged batch access, path-scoped staged run!, and committed retrieval from one selected responder. | blank read-only use!, put!, copy!, use-batch!, put-batch!, copy-batch!, run!, retrieve, retrieve-batch |
| Local retention/policy/admin | Authenticated and always mutated at Self; outward federation context is rejected. | pin!, unpin!, pin-batch!, unpin-batch!, admin-only prune!, prune-batch!, truncate!, authorization, config, bridge, admin, window, and secret operations |
| Root/Admin | Root commands for runtime mutation and administrative lifecycle management. | *eval*, *call*, *step*, *set-secret*, *set-step*, *set-query* |
Function Reference
Section titled “Function Reference”Use this reference when building runbooks or on-call procedures. The same function names appear in Explorer/Workbench, automated scripts, and incident diagnostics.
Public Functions
Section titled “Public Functions”| Function | Purpose |
|---|---|
size | Current permanent-chain size |
synchronize! | Accept a reciprocal signed-head exchange and return the receiver’s signed head |
info | Return the public root-signed journal descriptor |
route | Follow committed bridge control state and return terminal routing/authentication material |
trace | Return only authorized public crypto/bridge proof structure rooted at an index |
trace-batch | Return one compact authorized union proof for paths sharing one history anchor |
Restricted Functions
Section titled “Restricted Functions”| Function | Purpose |
|---|---|
blank read-only use! | Read current Stage, locally or through a signed working route |
put! | Stage a write locally or through a signed working route |
retrieve | Retrieve exact committed content locally, through a canonical committed route, or from one explicitly routed responder’s permanently retained history |
run! | Execute a staged Scheme program with root/admin default authority or an independent recursive path-scoped run! grant; ownership and blank read-only use! do not imply it |
pin! / unpin! | Mutate only Self’s retained permanent proof state |
authorize! / deauthorize! / authorizations | Manage Self-local path-scoped blank read-only use!/put!/run!/retrieve grants; run! remains independent of ownership and blank read-only use! |
bridge! / delete-bridge! | Manage reciprocal bridges locally |
config / update-config! | Read or update local ledger configuration under local policy |
use-batch! / put-batch! | Perform ordered snapshot reads or atomic prewrite-snapshot mutations locally or over one signed working route; conditional writes require both read and write authority for every member |
copy! / copy-batch! | Copy raw staged files or complete directories from one pre-mutation snapshot; optional target expectations add read authority requirements |
truncate! | Irreversibly release locally available history through an inclusive cutoff as Root or a configured local Interface administrator; grants and federation cannot confer it |
prune! / prune-batch! | As Root/configured local administrator, remove selected committed leaves/directories from both temporary and permanent retention while preserving Stage and committed history identity; grants and federation cannot confer it |
retrieve-batch / pin-batch! / unpin-batch! | Retrieve ordered committed paths, including one explicitly routed responder-local permanent view, or atomically mutate origin-local retention |
*secret* | Rotate the local interface secret/key material |
*admins-get* / *admins-set* | Read/replace local administrator principals |
*window-set* | Update the local public retention window |
Deleting a bridge removes its active and retired relationship state but preserves alias-scoped authorization rules and unused signing-key preapprovals. No permanent alias-to-Journal binding remains. Reusing an alias creates an ordinary new relationship, and the existing route-shaped grants apply to that alias deliberately and unchanged.
Root/Admin Functions
Section titled “Root/Admin Functions”| Function | Purpose |
|---|---|
*eval* | Evaluate arbitrary Scheme in admin context |
*call* | Invoke function with root object |
*step* | Execute full step cycle |
*set-secret* | Rotate root/admin secret |
*set-step* | Replace step handler |
*set-query* | Replace query handler |
Root Plane API
Section titled “Root Plane API”root.scm provides authentication of privileged operations, enforces root mutation/update discipline, and defines hook points for query and step behavior.
Root Object Methods
Section titled “Root Object Methods”These are the public root methods exposed by the root module’s base record object.
| Method | Signature | Description |
|---|---|---|
blank read-only use! | ((root 'get) path) | Read value at path; returns value, (nothing), or a directory listing. |
put! | ((root 'set!) path value) | Write value at path; supports structured node/object values and deletion via (nothing). |
copy! | ((root 'copy!) source target) | Copy source to target; a missing source deletes the target. |
equal? | ((root 'equal?) source target) | Exact structural equality check between two paths. |
equivalent? | ((root 'equivalent?) source target) | Digest-equivalence check between two paths. |
Root Commands
Section titled “Root Commands”These are public root commands handled by the transition function in root.scm.
| Command | Signature | Description |
|---|---|---|
*eval* | (*eval* <admin-secret> <expression>) | Evaluate expression in admin context. |
*call* | (*call* <admin-secret> <function>) | Invoke function with root object and persist resulting state. |
*step* | (*step* <admin-secret>) | Execute configured step handler pipeline. |
*set-secret* | (*set-secret* <old> <new>) | Rotate admin secret used by root plane. |
*set-step* | (*set-step* <admin-secret> <step-function>) | Replace step handler logic. |
*set-query* | (*set-query* <admin-secret> <query-function>) | Replace query handler logic. |
This API is intentionally powerful and low-level, so access should be tightly controlled and audited.
General API Bootstrap
Section titled “General API Bootstrap”interface.scm overlays the instantiated ledger object and dispatches ((function ...)) requests to ledger and bridge-helper methods, with permission checks on each call.
It also handles remote bridge fetch/merge flows before handing the final operation to ledger. A grant remains a normal recursive prefix grant. When a caller requests an ancestor directory of an applicable grant, the interface returns that directory’s ordinary immediate listing. This makes folder names traversable without synthetic parent grants, but reading or mutating any listed child still requires authorization for that child. Proof responses preserve the committed directory listing without granting access to child content.
General API Methods
Section titled “General API Methods”| Method | Permission | Description |
|---|---|---|
size, info | Public control | Return local public metadata. |
route, synchronize!, constrained trace | Public control | Operate on bridge/proof control structure, never as signed application calls. |
blank read-only use! | Authenticated/federatable | Read current staged content under terminal-local policy. |
put! | Authenticated/federatable | Stage a write under terminal-local policy; optional expected content makes it conditional and requires read authority. |
run! | Root/admin or explicit grant | Interface loads the current staged procedure once, evaluates it outside sync-let in a masked environment, and runs it non-atomically. Local non-admin and authenticated remote route principals require an explicit recursive run! rule; every nested operation preserves and rechecks the original caller before effects. |
truncate! | Local admin | Irreversibly release both local Ledger history representations through one inclusive positive/negative cutoff while preserving logical chain identity, indexes, Stage, retained suffix, and future append. |
prune!, prune-batch! | Local admin | Remove one path or a complete path union from both retained Ledger fields atomically; direct or ancestral committed *crypto* targets reject the complete call, while missing ordinary paths are idempotent and Stage/history roots, digests, signatures, size, and numbering remain unchanged. |
retrieve | Authenticated/federatable | Retrieve exact committed content and verify optional returned proof. An outward $federation.route selects one responder; that responder evaluates the path locally and requires permanent evidence. |
pin!, unpin! | Local authenticated | Retain/remove proof only in Self’s permanent chain; unpin rejects direct or ancestral committed *crypto* targets before mutation. |
use-batch!, put-batch! | Local authenticated | Read one staged snapshot or atomically write staged paths; optional expected values make every write conditional. |
copy!, copy-batch! | Local authenticated/federatable | Copy raw staged content atomically within one terminal Journal; optional target expectations require direct target read authority. |
retrieve-batch, pin-batch!, unpin-batch! | Authenticated paths; local mutation | Retrieve ordered committed paths or atomically mutate Self’s retained proof state. |
prune-batch! | Local admin | Atomically remove the union of up to 1,024 canonical committed paths from both retained fields. |
authorizations, authorize!, deauthorize! | Local authenticated | Inspect or mutate Self-local grants. |
bridge!, delete-bridge! | Local admin | Manage reciprocal bridges. |
config, update-config! | Local admin | Inspect or mutate local ledger configuration. |
*secret*, *admins-get*, *admins-set*, *window-set* | Local admin | Manage local interface identity and policy. |
Root commands (*eval*, *call*, *step*, *set-secret*, *set-step*, *set-query*) remain Journal-local in the standard deployment. Gateway receives only INTERFACE_SECRET and does not expose the Root plane.
This keeps externally visible behavior stable while allowing implementation details inside ledger classes to evolve.
Public ledger/interface paths use flat Scheme lists or JSON arrays, for example (*state* alice notes) and ["*state*", "alice", "notes"]. Nested path lists remain an internal ledger storage detail and should not be used in API requests.
Dedicated batch and conditional-write contract
Section titled “Dedicated batch and conditional-write contract”Every dedicated batch accepts at most 1,024 paths. Malformed, over-limit, and mismatched-cardinality requests fail before mutation. Empty read/write/retention batches are identity operations; trace-batch alone requires at least one path because it must select a history anchor. Results preserve request order and duplicates; a missing value is (nothing), not #f or (unknown). There is deliberately no arbitrary mixed batch! endpoint. Application-specific orchestration beyond the dedicated copy operations belongs in staged run! programs under Root/configured-admin default authority or an explicit recursive path-scoped run! rule.
use-batch! reads every staged path from one snapshot. retrieve-batch groups complete origin-relative paths by exact route and history anchor, or sends one complete batch to one explicit $federation.route responder for permanent-only local interpretation. Mixed responder contexts are not representable and are rejected. It verifies one compact proof per compatible group and returns only ordered content plus optional origin-local pinned? status. trace-batch requires one history anchor and returns the compact serialized union proof itself. pin-batch! retrieves and verifies all required remote proof groups before one atomic Self-local retention mutation; unpin-batch! validates its complete list, rejects direct or ancestral committed *crypto* targets, then atomically applies permanent-only digest-preserving cuts. Administrator-only prune-batch! uses the same protected-path validation, prepares both complete retained-field candidates, and installs both or neither; duplicates and ancestor/descendant overlaps equal their union. Any malformed, protected, or failed member rolls back the entire retention change.
Linear and log Chain objects expose read-only indices, returning (chain (<ascending materialized indexes>) <complete?>) without evaluating payloads. A trailing retained bridge alias selects this inventory; an alias followed by an integer selects that payload, and continued traversal after an alias implies its -1 head. At a terminal retention provider, each explicit or implicit -1 selects the greatest materialized index in that containing permanent Chain; a newer temp-only or unknown logical tail is ineligible. The terminal constructs the response proof from the exact permanent object authenticated by the invocation. The origin anchors that returned proof digest, independently derives those selections through the proof’s authenticated embedded Chain implementation, and merges richer same-root route evidence only to verify content at those exact paths. Historical Chains without indices execute their own complete embedded Chain-size and element-read fallback inside sync-let; malformed results and evaluator exhaustion fail the complete request.
retrieve and retrieve-batch accept optional boolean index?. Absent or false preserves the existing response exactly. When true, each successful result appends indexes, a left-to-right list of nonnegative absolute indexes corresponding to every integer selector in the requested committed path. A scalar local result always has one index; a federated result has one origin index followed by one index per hop. Raw scalar content is wrapped as ((content VALUE) (indexes (...))). Existing scalar metadata keeps its order and indexes is last. Every batch item appends its own indexes after existing fields; the echoed path remains the original request, and order and duplicates are unchanged. Replacing the request’s integer selectors with the returned indexes produces the fixed path for the selected roots, subject to retention. Missing (nothing) and available (unknown) results still carry selected indexes; failures before root selection do not.
Conditional put! distinguishes an omitted expected field from every explicit value, including #f and (nothing). Conditional put-batch! requires one expected value per path. All expectations compare against the same pre-write Stage snapshot before any replacement is applied; duplicate paths therefore compare with the same old value, while successful replacements retain input order and the last duplicate wins. A mismatch returns #f without changing Stage or its transition history. Conditional writes require both exact blank read-only use! and put! authorization because equality is observable. Concurrent writers using the same expected value have one winner; colliding Journal evaluations retry against the new root and return #f after the winner commits.
Scheme examples:
((function use-batch!) (arguments ((paths ((*state* alice one) (*state* alice missing))) (expression? #t))) (authentication ((identity (*state* alice)) (credentials "secret"))))
((function put-batch!) (arguments ((paths ((*state* alice one) (*state* alice two))) (values (new-one new-two)) (expected (old-one old-two)) (expression? #t))) (authentication ((identity (*state* alice)) (credentials "secret"))))
((function retrieve-batch) (arguments ((paths ((-1 *state* alice one) (-1 peer-a -1 *state* bob shared))) (pinned? #t) (expression? #t))) (authentication ((identity (*state* alice)) (credentials "secret"))))Equivalent Gateway JSON:
{ "paths": [["*state*", "alice", "one"], ["*state*", "alice", "two"]], "values": ["new-one", "new-two"], "expected": ["old-one", "old-two"], "expression?": true}The Gateway exposes dedicated /api/v1/general/{use-batch!,set-batch,retrieve-batch,trace-batch,pin-batch,unpin-batch} routes. JSON and Scheme requests use the same argument shapes and receive the same Journal semantic errors. HTTP body, response, and timeout limits still apply independently of the 1,024-path cap.
Monitoring
Section titled “Monitoring”Gateway Prometheus metrics are internal deployment telemetry. In the Compose stack, scrape http://gateway/metrics from a collector attached to the private sync-net network. Gateway has no host-published port, and the public router returns 404 for exact /metrics requests in both HTTP and TLS modes. Migrate monitors that previously scraped router /metrics to the internal service endpoint or another explicitly private collector.
Operations
Section titled “Operations”This section provides common operational procedures. Each example includes both Lisp and JSON forms. These are direct calls that can be run manually, but they also serve as templates for operational automation.
Secret Rotation
Section titled “Secret Rotation”Secret rotation is the minimum operational hygiene task for any deployed environment and should be part of regular runbooks.
Rotate interface secret
Section titled “Rotate interface secret”((function *secret*) (arguments ((secret "new-password"))) (authentication ((credentials "old-password")))){ "function": "*secret*", "arguments": { "secret": {"*type/string*": "new-password"} }, "authentication": { "credentials": {"*type/string*": "old-password"} }}Rotate root/admin secret
Section titled “Rotate root/admin secret”(*set-secret* "old-admin" "new-admin")[ "*set-secret*", {"*type/string*": "old-admin"}, {"*type/string*": "new-admin"}]Runtime lifecycle and configuration
Section titled “Runtime lifecycle and configuration”Only Root or a configured local Interface administrator may configure the
procedure at (*state* *periodic*); ordinary path-scoped run! grants do not
confer periodic configuration authority. When an outer periodic step creates a
new Ledger index, Interface launches that program exactly once through nonblocking run! and
passes the committed zero-based index as its sole argument. Unchanged steps and
internal step continuations never launch it. Different indexes may
run concurrently and finish out of order; program effects retain ordinary
non-atomic run! semantics. Deleting the path disables later launches. This is
not a durable scheduler/outbox: process failure after commit but before detached
dispatch can lose that launch.
The bundled 1.5 record composition is not an in-place record-code migration
surface. Do not replace Standard, Tree, Chain, Ledger, Federation,
Authorization, Interface, or Root code in a persistent deployment through
*eval*, *call*, or direct root-layout mutation. Those root-plane operations
remain privileged implementation mechanisms, not a supported upgrade procedure.
Supported runtime administration uses the query-interface operations documented
above: secret and administrator changes, config/update-config!, retention
window changes, authorization rules, and reciprocal bridge management. Apply
record-code changes by creating a fresh database with the new release, then
validate service behavior before switching traffic.
Inspect current public configuration through the authenticated config call:
((function config) (authentication ((credentials "password")))){ "function": "config", "authentication": { "credentials": {"*type/string*": "password"} }}For a release transition, preserve the old database/runtime pair first, deploy a
fresh candidate under a separate Compose project and ports, run health and
semantic probes, and switch only after the fresh stack passes. Never use
JOURNAL_UPDATE=1 as a 1.4.x-to-1.5 conversion mechanism.