Architecture
This page is a tour of how a Volt4 is put together: the ways a client can reach one, how a request is authenticated and authorised, which subsystems handle it, and where the resulting data ends up.
If you are new to the platform, read fundamentals first — this page assumes you already know roughly what identities, resources and policy are for.
Open the diagram full size to zoom in.
The stack in the middle of the diagram is one running Volt. Layers are drawn in the order a request passes through them, and boxes are labelled with the component names used in the source so the diagram can be read alongside the code.
One process, many Volts
Two ideas are easy to conflate, and the diagram keeps them apart deliberately.
A Volt is the unit of identity and trust. It has its own key, its own certificate authority, its own policy and its own database. Everything on this page from the edge downwards describes a single Volt.
A Battery is the host that runs them. One operating-system process can hold several Volts at once, and the Battery is what keeps track of them: its database records each Volt’s id, name, address, ports and whether it should start automatically, it starts those Volts on launch, and it re-reads their configuration periodically so that changes made through one Volt are picked up by the others in the same host.
The Battery also owns the one listener that does not belong to any individual Volt: the discovery service, described under reaching a Volt below.
Reaching a Volt
A Volt exposes up to four listeners, and which of them are running depends on configuration.
The gRPC Volt API
This is the primary interface and the one all the first-party clients use. It is a single TLS port carrying every Volt RPC — resource and policy administration, file transfer, SQL, synchronisation, wires, SSI, drives, the terminal — so a client needs exactly one connection to a Volt regardless of what it intends to do. The port can be fixed in configuration or assigned by the operating system at start-up.
Two of its methods behave differently from the rest because they are long-lived bidirectional streams rather than request/response calls:
Connectkeeps a channel open so the Volt can push events to the client, and so the client can act as a service host — the Volt forwards inbound calls for a registered service down this stream to whoever is hosting it. See connect stream.Invokecarries calls that have been tunnelled through a relay, described under working through a relay.
See the Volt API reference for the full method list.
The HTTP server
When enabled, the HTTP server runs on its own port, which defaults to the gRPC port plus one. It is the way a Volt serves ordinary web traffic:
- the hosted dashboard, a single-page application built into the Volt binary;
- a REST API and WebSocket API mapped onto the same underlying gRPC calls, for clients that cannot speak gRPC;
- OIDC login, so browser sessions can be established against an external identity provider;
- static file servers and published folders, which expose file resources as plain HTTP;
- domain and path forwarders, which proxy requests on to other services.
Mail and terminal
Two further listeners are driven by individual resources rather than by global configuration. An SMTP server is started for each email resource that asks for one, and incoming mail is parsed into the resource tree. The terminal is a bidirectional gRPC stream that provides a remote shell on the host, gated by policy and available on macOS and Linux only — see execute a remote command.
Discovery
Discovery belongs to the Battery rather than to a Volt, and it is the one interface that accepts unauthenticated calls, on ports 54125 and 54126. It answers with the endpoint details of any Volt in the Battery that has been marked discoverable, signed so that the caller can verify which Volt it is really talking to. Relays offer an equivalent lookup for Volts that are not on the same network — see the Discovery API.
Authentication, then authorisation
These are two distinct steps in a Volt, handled by different components, and it is worth understanding them separately.
Authentication establishes who is calling. Every RPC is resolved to a live session using either a signed JWT presented in the call metadata, or the TLS client certificate used to set up the connection. A small number of methods are deliberately open at this stage — Authenticate, Invoke, CheckCompatibility and the DID resolve and register calls — because each of them enforces its own requirements internally, usually a signature over the request.
Authenticate is how a client obtains its credentials in the first place: the Volt issues a client certificate signed by its own certificate authority, along with the authority’s own certificate so the client can verify the Volt in future. That exchange can carry verifiable credentials, and the resulting session can be tied to a Volt-controlled DID, a user-controlled DID or an ephemeral key. See approve an authenticate request.
Authorisation then decides what that caller may do. It is an attribute-based engine modelled on XACML: the Volt composes a root policy from its built-in defaults plus the access grants stored in its database, and evaluates each request against it. Attributes are supplied on demand by information points for the subject, the resource and the environment, which is what allows rules to refer to things like the time of day or a resource’s position in the hierarchy. Administrative calls are checked as privileged API permissions rather than as access to a resource. See policy and policy definition.
The Volt’s own private key never has to live on disk in the clear; where it comes from is chosen per Volt by its key strategy, which can be a file, a password-protected file, a PKCS#11 token or hardware.
The control plane
The control plane is the set of components that decide what the Volt is currently running. The most important thing to understand about it is that almost everything is configured as a resource: a database, a wire, a published folder, an SMTP server, a relay connection and a drive instance are all resources in the same tree.
That makes the event bus central. When a resource is created, changed or deleted, the change is published in-process, and the services that care about that kind of resource start, reconfigure or stop themselves in response. Adding a wire or a relay connection therefore takes effect without restarting the Volt.
The rest of the control plane follows from that: resource and identity managers maintain the tree and the identities in it (and ask for a policy reload when a change affects access), the service registry indexes hosted services along with the protobuf descriptors needed to call them, the connection manager owns the Connect streams and the forwarding of calls across them, and the relay and HTTP managers own their respective outbound connections and listeners.
The data plane
The data plane is where requests that carry actual payloads are handled. Each service corresponds to one or more resource kinds.
Synapse, the collaborative data layer
Of everything in the data plane, synapse is the piece to understand first, and it is the capability the rest of the platform leans on most heavily.
A sync database holds many documents, and each document is a Yjs CRDT with a declared schema: map, array or text roots for structured and collaboratively edited data, or a blob document for a binary payload, whose bytes live in a separate content-addressed store keyed by their SHA-256 hash. Because every document is a CRDT, concurrent edits from different clients merge rather than overwrite each other.
A synapse is a sync database that also replicates between Volts. A synapse connection links a synapse on one Volt to a synapse on another; from then on each Volt watches the underlying database for changes and replicates them in both directions continuously, retrying until the connection succeeds. That is what makes a dataset genuinely shared rather than merely shareable — the same documents are live on every Volt and client that participates, and no single Volt is the bottleneck.
Clients reach it either through the plain Yjs API using SyncProvider, or through the synapse API directly, writing and watching individual JSON paths — wildcards included — over a stream. Drives are built entirely on this machinery, storing text files as Yjs text documents and binary files as blobs.
The other data services
- Files — upload, download and streaming of file content, stored in blocks that can be encrypted at rest.
- SQL — a database or database view per resource, with execute, bulk load and CSV import.
- Wires — publish and subscribe wire streams, with optional persistence so subscribers can catch up.
- SSI — DIDs and verifiable credentials, a local record of DID operations, and polling of remote registries.
- Drives — folder synchronisation between a drive and a local directory on the host.
- Email — inbound parsing and outbound sending.
Persistence
A Volt keeps everything on the local filesystem. There is no external database server to run.
- The Volt database holds one Volt’s state: its resources and their structure and attributes, identities, sessions, access grants, custom policy and the DID operation log. It is a SQLite database, or an encrypted SQLCipher one when the Volt is protected by a passphrase.
- The resource store holds file content as opaque blocks on disk, optionally encrypted with the Volt’s storage key.
- Per-resource SQLite files hold the bulk data that does not belong in the Volt database: synchronisation updates, wire history, database resources and blob stores.
- The Battery database sits alongside them and holds the catalogue of Volts on the host, as described in Battery.
Working through a relay
A Volt on a home or office network usually cannot accept inbound connections. The relay model solves that without any special network configuration: the Volt dials out to a relay — itself an ordinary Volt that happens to have a public address — and holds that connection open. Calls for the first Volt then arrive over the connection it already established.
Client calls made this way are tunnelled through the Invoke method, carrying a signed JWT and a payload encrypted for the recipient, so the relay routes traffic without being able to read it. A relay also acts as a directory: a client can ask it which Volts are currently registered, which is how Volts find each other when they are not on the same network. A Volt can be connected to several relays at once.
See relay for the trust model in more detail, and the Relay API for the lookup call.
Further reading
- Fundamentals — the four central facets of the platform.
- Battery — the host that stores and runs Volt configurations.
- Configuration — the settings referred to throughout this page.
- Policy and policy definition — the authorisation engine and its rule format.
- Connect stream — the long-lived channel used for events and service hosting.
- Relay — reaching Volts behind a firewall.
- Command line, Native / C++ and Web — the clients shown at the top of the diagram.