The mesh nodes, holding the log.
NovusLens keeps what it learns about an organisation in one place first: an ordered log held by a small set of MyceliumMyceliumThe application infrastructure underneath NovusLens. Its connected services exchange updates and keep the ordered structural log. nodes. The projectionProjectionThe regular pass that brings the read model up to date with the canonical log. It runs every 30 seconds by default. worker turns that log into the PostgresPostgresThe database software that stores the tables read by the NovusLens screens, along with document bodies, access records and protected personnel records. read modelRead modelThe database tables used by the map, Ask and Find. NovusLens rebuilds these tables from its history of recorded changes. that people query. If the read model is lost, the log can rebuild it. This page explains what the nodes are, how they are started and pinnedPinA recorded choice of one exact dependency version. It prevents an update from changing that dependency without a deliberate decision., and the rules the code follows when it writes to them.
What it is for
Structural changes enter the mesh log. Some other records, including document bodies and personnel links, are stored directly in Postgres. The mapMapThe view of teams, services and their dependencies for the selected part of the organisation. Details appear beside the map., the C/A/RC/A/RThree separate scores for a team or service: Complexity, Agility and Recoverability. Each uses current evidence and the customer’s stated scoring rules. (Complexity, Agility and RecoverabilityRecoverabilityThe R in C/A/R: how well a team or service restores acceptable service after a failure. Recovery times and tested alternatives can supply evidence.) figures and the findingsFindingA difference in the records that needs a decision. It includes its sources and can describe a slow handoff, disputed fact or missing owner. all derive from that log. So the mesh decides whether a twinTwinNovusLens’s model of one organisation, built from records about its teams, services and how they rely on one another. It is only as current as those records. survives a restart, and whether a backup is complete. Every part of NovusLens reads from the same recorded history.
How it works
Three nodes, one sidecar binary
Mycelium is the shared storage layer. Its nodes exchange updates directly, without a separate message broker. NovusLens does not link it into its own processes. It runs Mycelium’s three_node_demo as a separate sidecarSidecarA separate supporting process that runs beside the application. Here it runs Mycelium and exposes the gateway that NovusLens calls. process. NovusLens calls its HTTPHTTPHypertext Transfer Protocol: the rules a browser or program uses to send a request to a web service and receive its response. gatewayGatewayA service that accepts requests and passes them into another system. The Mycelium gateway gives NovusLens access to the mesh. through the Python software development kitSDKSoftware development kit: a package of code that helps another program use a service. NovusLens uses mycelium-py to talk to Mycelium. (SDK), mycelium-py.
Docker ComposeDocker ComposeThe Docker tool that starts and connects the services listed in one configuration file. starts three sidecar services, mesh-0 to mesh-2. Each node i gets its own settings:
| Setting | Value for node i |
|---|---|
MYCELIUM_ROLE | overlay |
MYCELIUM_PORT (gossipGossipA way for connected services to pass updates to one another until the updates spread through the group.) | 57010 |
MYCELIUM_HTTP_PORT (gateway) | 8400 |
MYCELIUM_HTTP_ADDR | 0.0.0.0, inside the private backend network |
MYCELIUM_GATEWAY_TOKEN | the shared bearerBearer tokenA secret value sent with a request to prove that the caller holds it. Anyone who obtains it can use it, so it must be protected., MYCELIUM_GATEWAY_TOKEN |
MYCELIUM_PEERS | the other two nodes’ gossip addresses, for example mesh-1:57010 |
MYCELIUM_DATA_DIR | /data, on the volume mesh-<i>-data |
Compose checks /health on every node, not only node 0. The clients (TwinWriter, the reasoning service and the projection loop) talk to node 0, mesh-0, on NOVUSLENS_MESH_HTTP_BASE (8400).
Durable by directory
Each node has its own data volume (MYCELIUM_DATA_DIR), holding a write-ahead logWrite-ahead logA file that records changes before they are reflected in stored data, so the changes can be restored after a restart. (WAL) and snapshot. The patched HTTP append waits for the configured write-ahead log before acknowledging a commit. Customer Flush mode waits for the write and fsyncfsyncAn operating-system call that asks for pending file changes to be saved to disk before the program carries on.. A writer failure returns HTTP 503 without an HLCHybrid logical clockA number attached to a log entry to order it against other entries. It combines clock time with a counter, so entries can be ordered even when clocks do not agree exactly. success, before gossip. The preceding memory update is not rolled back. Without a data directory, storage is ephemeral; other sync modes retain their configured weaker acknowledgement semantics. The development driver ./lens ops runs the sidecar without a data directory, so a fresh log starts for each gate. The Compose installation never does that. ./lens backup archives the database and state volumes under the installation. See Deployment and gates.
The pinned SDK and the built sidecar
Neither half of the storage layer comes from a neighbouring checkout.
- The SDK is a pinned wheelWheelA packaged Python library ready to install. A vendored wheel is stored in this repository so its exact bytes can be checked..
mycelium-pyis installed fromvendor/mycelium_py-0.2.4-py3-none-any.whl, built from the tag recorded inconfig/mycelium.pin(the full commit SHAGit commitA recorded set of changes. In a software repository it identifies a code version; in the twin log it records a change to the model or its evidence., Mycelium v2.4.4). - The shared Mycelium base builds the pinned sidecar once. All five application images and the controller inherit it.
./lens ops --version dev sidecarcopies the image binaryBinaryA program file built to run on a particular kind of machine. A program built for one processor may not run on another. into.sidecar/; no host checkout is compiled. - The manifestRelease manifestThe file recording the exact software versions and fingerprints used for a NovusLens release candidate. Installation checks compare files against it. records what the gates ran on.
config/release-manifest.jsonholds the wheel and sidecar SHA-256SHA-256A method that turns content into a fixed-length fingerprint. Comparing fingerprints helps check whether content changed. hashesHashA fixed-length fingerprint calculated from content. Comparing fingerprints helps detect changes and match records..
The gateway patch
The upstream example binds its HTTP gateway to 0.0.0.0 with no authentication. The gateway carries the canonical logCanonical logThe permanent history of changes to an organisation’s model. New entries are added at the end. Earlier entries stay when the database is rebuilt., key-value storeKey-value storeStorage where a named key points to a value. Reading that key retrieves the value currently stored under it. and Model Context ProtocolMCPModel Context Protocol: a standard way for an AI application to discover and call named tools supplied by another program. (MCP) tool path. NovusLens patches its bind address, bearer handling and log acknowledgement. The bind address comes from MYCELIUM_HTTP_ADDR (default 127.0.0.1). When MYCELIUM_GATEWAY_TOKEN is set, every request except /health, /ready, /stats and /metrics must carry it as a bearer. Compose binds each gateway to 0.0.0.0 inside the private backend network and publishes no mesh port to the host.
Key namespace rules
All NovusLens state in the key-value store lives under conway/. The storage layer reserves other top-level prefixes (grp/, sys/, consensus/, cap/, audit/, log/ and others). In conway_mesh/__init__.py, RESERVED_PREFIXES holds 30 of them: the 26 in the Mycelium’s own table at the pinned version, plus four the guard keeps avoiding to be safe. Two functions enforce the rule:
key(*parts)builds aconway/…key. Each part must be non-empty and contain no slash.assert_ours(k)raisesReservedPrefixErrorif a key falls under a reserved prefix or outsideconway/.
One write is deliberately outside the namespaceNamespaceA naming area that keeps one set of records apart from another. NovusLens uses the conway/ prefix so its keys do not occupy Mycelium's reserved names.: the Mycelium’s own MCP registration convention tools/<name>/<node>, built by keys.mcp_tool_registration. Key shapes are covered in The twin model and keys.
Consistency: eventual by default
Writes are eventually consistentEventual consistencyConnected copies of data may briefly differ after a write. They move towards the same state as updates reach them. by default (last-writer-winsLast-write-winsA rule for competing updates in which the update with the latest ordering stamp supplies the stored value.). Observed telemetryTelemetryMeasurements and activity records sent by a running system, such as ticket timings, network traffic or workflow results. is plain key-value with a refresh interval, and its freshness is a read-side convention: the store never evicts by age. ConsensusConsensusA process in which connected services agree on one result before accepting it as a shared decision. is used only where linearisabilityLinearisabilityAccepted operations behave as if they happened one at a time in a shared order. That order respects operations that already finished. is needed, in the correctorCorrectorThe NovusLens component that compares measurements with recorded statements and adds observations and differences to the log. (apps/connectors/src/conway_connectors/corrector.py). Writes have a size limit, so entries stay small: attributes and pointers go in the mesh, and evidence bodies go to Postgres.
Structural changes enter the ordered log
Structural changes to the twin are not consensus decisions. They are appended to a per-organisation log stream, conway/commit/<org>, by agent.append(), which returns a hybrid logical clock (HLC) stamp. The projection worker reads the stream using scan_log(from_hlc). It saves each applied batch and its cursorCursorA saved position in a sequence of records. A program uses it to continue from where it stopped. in one Postgres transactionTransactionA group of database changes that is saved together. If the group fails, its changes are not saved., allowing restart from that position. The same log is the audit trail. Event shapes are in Building and committing the twin.
When ownership or structure is contested, NovusLens surfaces a contested-structure finding and a person resolves it. The tool does not use consensus to choose between those accounts.
The fencing token
The corrector takes a distributed lockDistributed lockA lock that coordinates programs running in separate processes or on separate machines, so only one holder is recognised at a time. (conway-correct-<org>) only long enough to read a fencing tokenFencing tokenA number that identifies a turn to update shared data. NovusLens rejects an earlier correction’s writes after a newer correction is applied.. That token is carried on the observed-layer edge, C/A/R and flow events it then appends (an optional fencing_token field in commits.py, which the corrector supplies). The materialiserMaterialiserThe component that reads recorded changes in order and updates the database. It records its place so it can continue after a restart., in apps/graph/src/conway_graph/materialiser.py, keeps the highest token per organisation in the correction_epoch table. A write carrying a lower token raises StaleCorrectionError and is quarantinedQuarantineA holding area for a log entry that could not be applied. The entry keeps its error and waits for a retry or an operator.. So a stale lock holder cannot overwrite a newer pass. The consistent_set record the corrector also writes is a linearisability demonstration only. Nothing reads it back.
Interfaces
| Caller | What it uses |
|---|---|
| Docker Compose | The sidecar services, started with the settings above, and /health on each node. |
TwinWriter and connectorsConnectorSoftware that reads records from a source system, such as an incident tool. A customer-written file connects those records to teams or services in NovusLens. | The Python SDK against node 0’s gateway, with the gateway bearer. |
| Projection worker | scan_log on conway/commit/<org>. |
| Sovereignty | One mesh clusterClusterA group of computers running together as one installation. Each regional NovusLens installation keeps its own records. per region. Signals flood across a cluster, so regions are separate clusters, never boundaries inside one. |
Invariants and failure behaviour
- Nothing under a reserved prefix is written by NovusLens code, except the one MCP registration key.
- Compose refuses to start without
MYCELIUM_GATEWAY_TOKEN, and setsCONWAY_PROFILE: customerfor every service. - Compose restarts a service that exits (
restart: unless-stopped). - A restart replays the persisted log. The projection cursor anchor check confirms the log came back after a restore.
forget-orgerases an organisation’s read model, but the canonical log still carries its commits. Erasing those means destroying the mesh data directories.
The commit stream, correction lock and fencing token, projection worker, materialiser, and the Postgres read model with its cursor and quarantine.