Skip to content

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.

FolderWhat 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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 serverStays on the device
The auth key (the server hashes it again)The master password
Ciphertext of E2E valuesThe encryption key
Signed ops and public keysPrivate keys and folder keys
Values of server-readable itemsDecrypted 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

Last updated on