Architecture
This page shows how the parts of Lasbolt fit together. The full contract is the design contract. The goals are in GOAL.md.
The idea
Lasbolt is a self-hosted password manager. One server is one organisation. All content is a log of signed operations (ops). The current state of an item is derived from the log. Each client keeps its own copy of the log, works offline, and merges when it connects.
There are two kinds of content:
- Server-readable. The server can read the values. It uses this to search, to serve the API, to serve agents and to merge. The server encrypts these values at rest with a data key that only the running server holds.
- End-to-end (E2E) folders. The server cannot read the values. Only the clients that have the folder key can.
Components
The repository is a set of Go modules and native apps. A root go.work joins the modules.
| Folder | What it is |
|---|---|
core/ | Shared Go code for the server and all clients. |
core/pwcrypto/ | All cryptography. No other package uses a crypto primitive. |
core/oplog/, core/oprules/ | The op type, the hybrid logical clock, the signing bytes and the merge rules. |
core/sync/ | The sync engine, the wire types of the API and the HTTP client. |
core/e2e/ | End-to-end folders, keys, certs, checkpoints and the membership rules. |
core/bind/ | The WASM library for the web app, and the key logic behind it. |
server/ | The server lasboltd, the control tool lasboltctl and the test harness. |
client/ | The command line client lasbolt, the local agent and the MCP server. |
extension/ | The browser extensions for Chrome and Firefox. |
apple/ | The macOS app, the iOS app, AutoFill and the Safari extension. |
android/ | The Android app. |
The server and the clients use the same wire types from core/sync, so they cannot drift apart.
Data flow
flowchart TB
subgraph Clients
WEB["Web app<br/>(htmx and a WASM island)"]
CLI["lasbolt CLI"]
EXT["Browser extensions"]
APP["macOS, iOS and Android apps"]
MCP["MCP server for AI agents"]
end
AGENT["Local agent<br/>(keys and local store)"]
subgraph CoreLib["core (shared Go code)"]
SYNC["sync engine and oplog"]
E2E["e2e"]
CRYPTO["pwcrypto<br/>(the crypto boundary)"]
end
subgraph Server["lasboltd"]
API["Sync API /api/v1"]
WEBH["Web app handlers"]
STORE[("SQLite store<br/>encrypted at rest")]
SEAL["seal<br/>(data key, key provider)"]
end
CLI --> AGENT
EXT --> AGENT
APP --> AGENT
MCP --> AGENT
AGENT --> SYNC
WEB --> E2E
SYNC --> E2E
E2E --> CRYPTO
SYNC --> CRYPTO
SYNC -->|"HTTPS: signed ops, ciphertext"| API
WEB -->|"cookie session"| WEBH
API --> STORE
WEBH --> STORE
STORE --> SEAL
SEAL --> CRYPTO
Read the diagram from the left:
- A client reads and writes its local store. The CLI, the extensions and the apps go through the local agent. The agent holds the keys and the store, so a short-lived process does not need to unlock anything.
- The sync engine pushes new ops to the server and pulls the ops of other devices. Each op is signed. A push is a set of groups, and each group succeeds or fails as one unit.
- The server checks the shape and the signature rules, stores the ops, and tells other devices with a server-sent event that a space changed. It never sends data in an event.
- A device may also sync with other devices of the same user, with or without the server. This is peer sync.
The web app is different. The server renders it with htmx. A small WASM library in the browser does the client-side crypto for the login and for E2E views.
The crypto boundary
The cryptography has one home: core/pwcrypto. It has the password KDF (Argon2id), the symmetric cipher, the public-key sealing, the signatures and TOTP. A change to it needs new test vectors and a review.
What crosses the line between a client and the server:
| Crosses to the server | Stays on the device |
|---|---|
| The auth key (the server hashes it again) | The master password |
| Ciphertext of E2E values | The encryption key |
| Signed ops and public keys | Private keys and folder keys |
| Values of server-readable items | Decrypted E2E values |
The server must never get the master password or the encryption key.
The server has its own key hierarchy for data at rest. A key provider (passphrase, file, or insecure-static for tests) gives a key that unwraps one data key. The data key exists only in memory. Backups use the same hierarchy, or are sealed to a separate public key.
Access for tools and AI agents
Tools and AI agents do not use the master password. They use a device-code session or a scoped key. A scope limits the access level, the folders and the items. An item can ask for an approval at each use. The user then approves a read in the web app. The CLI hides secret values in its output, and the MCP server never returns a secret value. See AI agents.
Where to read more
- Design contract: the shapes, the crypto and the APIs in full.
- Threat model: what Lasbolt defends against.
- Reference: the commands, the routes, the ops and the error codes.
- Build and test: how to build and check the code.