Skip to content

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.

This section describes how to bring up and manage the reference runtime stack used throughout the rest of this guide.

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 names
  • SECRET (required): Root secret supplied only to Journal
  • INTERFACE_SECRET (required): independently rotatable Interface bearer supplied to Journal and Gateway and persisted in private Root state; it must differ from SECRET throughout installation and rotation
  • INTERFACE_ADMINS (default admin): comma-separated local usernames installed as (*state* <name>) administrators on a fresh database
  • ADMIN_USERNAME / ADMIN_PASSWORD: independent identity-provider bootstrap account
  • HTTP_PORT (default 8192): published HTTP port
  • HTTPS_PORT (default 8193): published HTTPS port
  • PERIOD (default 2): stepping cadence parameter passed to journal
  • WINDOW (default 1024): retention window for unpinned history
  • TLS_CERT_HOST_PATH (optional): host certificate file mounted into router
  • TLS_KEY_HOST_PATH (optional): host key file mounted into router

Start:

Terminal window
COMPOSE_PROJECT_NAME=sync-local SECRET=root-password \
INTERFACE_SECRET=interface-password ADMIN_PASSWORD=admin-pass \
tests/api/local-compose.sh up

Smoke validation:

Terminal window
COMPOSE_PROJECT_NAME=sync-local tests/api/local-compose.sh smoke

Direct local HTTP deployment (no TLS):

Terminal window
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 -d

Optional public TLS deployment with the same compose file:

Terminal window
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 -d

Stop without deleting data:

Terminal window
COMPOSE_PROJECT_NAME=sync-dev docker compose -f deploy/compose/general/compose.yaml down

Only add -v when you intentionally want to delete that stack’s database and identity-provider volumes.

The default stack includes:

  • journal runtime (journal-sdk plus 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. Sync Web 1.5 supports fresh database installation only. Its installer rejects every nonfresh root atomically, so JOURNAL_UPDATE=1 cannot update an existing database with this release line. Compose startup accepts only the exact successful installer result. A process failure, Scheme error value, or malformed result exits nonzero before writing the fresh 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.

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 journal identity.

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:

RoutePurpose
/interfaceScheme or JSON request endpoint selected by request Content-Type.
/docsGateway 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.

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.

FamilyDescriptionFunctions
Public bridge/control/proofDirect protocol operations with no signed application invocation. Outward Gateway $federation context is rejected.size, info, route, synchronize!, constrained trace, trace-batch
Authenticated applicationLocal calls use session/API-token authentication; signed federation is limited to scalar staged access, dedicated staged batch access, and committed scalar resolution.get, set!, get-batch, set-batch!, resolve
Authenticated committed batchGateway outward federation context is rejected; Interface groups canonical committed paths and routes compatible remote groups internally.resolve-batch
Local retention/policy/adminAuthenticated and always mutated at Self; outward federation context is rejected.pin!, unpin!, pin-batch!, unpin-batch!, authorization, config, bridge, admin, window, and secret operations
Root/AdminRoot commands for runtime mutation and administrative lifecycle management.*eval*, *call*, *step*, *set-secret*, *set-step*, *set-query*

Use this reference when building runbooks or on-call procedures. The same function names appear in Explorer/Workbench, automated scripts, and incident diagnostics.

FunctionPurpose
sizeCurrent permanent-chain size
synchronize!Accept a reciprocal signed-head exchange and return the receiver’s signed head
infoReturn the public root-signed journal descriptor
routeFollow committed bridge control state and return terminal routing/authentication material
traceReturn only authorized public crypto/bridge proof structure rooted at an index
trace-batchReturn one compact authorized union proof for paths sharing one history anchor
FunctionPurpose
getRead current Stage, locally or through a signed working route
set!Stage a write locally or through a signed working route
resolveResolve exact committed content locally or through a signed route/history cursor
call!Execute a staged Scheme program locally as the root caller or a configured Interface administrator; rules and ownership cannot grant it
pin! / unpin!Mutate only Self’s retained permanent proof state
authorize! / deauthorize! / authorizationsManage Self-local path-scoped get/set!/resolve grants
bridge! / delete-bridge!Manage reciprocal bridges locally
config / update-config!Read or update local ledger configuration under local policy
get-batch / set-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
resolve-batch / pin-batch! / unpin-batch!Perform grouped committed resolution and atomic origin-local retention changes without outward Gateway federation context
*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 route/configuration and associated grants but retains the alias↔journal-root identity binding. Re-establishing the same alias/root is valid and idempotent; substituting another root or moving the root to another alias is rejected as an identity conflict.

FunctionPurpose
*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.scm provides authentication of privileged operations, enforces root mutation/update discipline, and defines hook points for query and step behavior.

These are the public root methods exposed by the root module’s base record object.

MethodSignatureDescription
get((root 'get) path)Read value at path; returns value, (nothing), or a directory listing.
set!((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.

These are public root commands handled by the transition function in root.scm.

CommandSignatureDescription
*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.

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.

MethodPermissionDescription
size, infoPublic controlReturn local public metadata.
route, synchronize!, constrained tracePublic controlOperate on bridge/proof control structure, never as signed application calls.
getAuthenticated/federatableRead current staged content under terminal-local policy.
set!Authenticated/federatableStage a write under terminal-local policy; optional expected content makes it conditional and requires read authority.
call!Local adminInterface loads the current staged procedure once, evaluates it outside sync-let in a masked environment, and runs it non-atomically as root or a configured local Interface administrator. Namespace owners, policy grantees, and remote principals are denied before program loading.
resolveAuthenticated/federatableResolve exact committed content and verify optional returned proof.
pin!, unpin!Local authenticatedRetain/remove proof only in Self’s permanent chain.
get-batch, set-batch!Local authenticatedRead one staged snapshot or atomically write staged paths; optional expected values make every write conditional.
resolve-batch, pin-batch!, unpin-batch!Authenticated paths; local mutationResolve ordered committed paths or atomically mutate Self’s retained proof state.
authorizations, authorize!, deauthorize!Local authenticatedInspect or mutate Self-local grants.
bridge!, delete-bridge!Local adminManage reciprocal bridges.
config, update-config!Local adminInspect or mutate local ledger configuration.
*secret*, *admins-get*, *admins-set*, *window-set*Local adminManage 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 and no general copy endpoint. Application-specific orchestration belongs in administrator-only staged call! programs.

get-batch reads every staged path from one snapshot. resolve-batch groups complete origin-relative paths by exact route and history anchor, verifies one compact proof per compatible remote 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! resolves and verifies all required remote proof groups before one atomic Self-local retention mutation; unpin-batch! atomically applies digest-preserving local cuts. Any malformed or failed member rolls back the entire retention change.

Conditional set! distinguishes an omitted expected field from every explicit value, including #f and (nothing). Conditional set-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 get and set! 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 get-batch)
(arguments ((paths ((*state* alice one) (*state* alice missing)))
(expression? #t)))
(authentication ((identity (*state* alice)) (credentials "secret"))))
((function set-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 resolve-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/{get-batch,set-batch,resolve-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.

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.

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 is the minimum operational hygiene task for any deployed environment and should be part of regular runbooks.

((function *secret*)
(arguments ((secret "new-password")))
(authentication ((credentials "old-password"))))
(*set-secret* "old-admin" "new-admin")

A configured local Interface administrator may stage one procedure at (*state* *periodic*). When an outer periodic step creates a new Ledger index, Interface launches that program exactly once through nonblocking call! 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 call! 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"))))

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.