Velnode/ Docs / Architecture Overviewv2.3.3-beta.15
๐Ÿ“ฆ This guide also ships inside Velnode โ€” open the โ˜ฐ menu โ†’ Help in the browser; it works offline.

Velnode

Architecture Overview

Velnode Browser  ·  a product of Dignity New Zealand Limited

Product  |  Version 1.2  |  Last updated 2026-07-03

Velnode


Architecture Overview

Document type: Product Owner: Dignity New Zealand Limited Applies to: Velnode Browser v2.3.2-beta.1 and later Last updated: 2026-07-03 Document version: 1.2 Status: Approved


This document gives engineers, security reviewers, and partner integrators a structural view of Velnode Browser. It is one level deeper than How It Works and one level shallower than the canon manifest and source code.

Velnode Browser is a product of Dignity New Zealand Limited. The source code is proprietary and source-available; all rights are reserved by Dignity New Zealand Limited.

1. Process model

Velnode is an Electron 37 application. Electron's two-tier process model is preserved unchanged:

The renderer is hardened with nodeIntegration: false, contextIsolation: true, sandbox: true. The renderer's only privileged channel is the preload bridge in preload.js, which exposes a small allow-listed API to window.veloraBridge.

2. IPC surface

preload.js exposes one object to the renderer, window.veloraBridge. Every method on it corresponds to a named IPC channel whose handler is registered in main.js. No additional channels are reachable. Renderer code that tries to invoke an unregistered channel receives an error in the main process and a refusal in the renderer.

The discipline is: new feature โ‡’ named channel โ‡’ explicit allow-list entry โ‡’ explicit handler โ‡’ explicit permission check. The order matters; we never add a handler without a permission check, and we never add a channel without a handler.

3. AI Gateway

All AI requests originate in the renderer and are routed to the main process over the vee:request channel. The Gateway:

  1. Loads the active policy from data/settings.json (Kids Mode lock, Local-Only mode, token caps).
  2. Evaluates the policy against the request. If the request would breach the policy, the Gateway short-circuits with a refusal and writes a timeline entry explaining why.
  3. Selects a provider:
    • Cloud provider (OpenAI-style HTTPS endpoint) โ€” keys read from main-process secure storage; never visible to the renderer.
    • Local LLM brain โ€” invoked via node-llama-cpp against a model the user has explicitly downloaded.
  4. Streams the response back over the same channel. The renderer renders it in whatever surface initiated the call.

There is one Gateway. There is one personality wrapper (the Vee system prompt). The renderer cannot bypass either, because the renderer has no direct network access (webRequest is brokered in the main process) and no provider credentials.

4. Policy layer

The policy layer is a pure function policy(settings, request) โ†’ decision. It is exercised by the Gateway on every call. Its inputs are the current settings.json snapshot and the request envelope; its output is one of {allow_cloud, allow_local_only, refuse_with_reason}.

Kids Mode and Local-Only mode are not UI states; they are policy inputs. Turning them on in the renderer is, in effect, asking the main process to honour them. Once on with the parental code set, the toggle is locked: the renderer can read the state but cannot flip it.

5. File-backed state

Velnode keeps no application state in localStorage, sessionStorage, IndexedDB, or in-memory caches that are not derivable. State lives in plain JSON files under data/:

File Append-only Notes
data/timeline.json Yes Canonical record. Never edited, only appended.
data/semantic-memory.json No Derived. Rebuilt deterministically from the timeline.
data/bookmarks.json No User bookmarks and folders.
data/notes.json No Sticky notes attached to tabs or URLs.
data/settings.json No All user-facing settings, including Kids Mode lock.
data/command-pins.json No Pinned Vee commands.
data/mindmap.json No Mindmap nodes and edges.
data/plugins-enabled.json No Plugins explicitly enabled by the user.

In a packaged installation the data/ folder lives inside Electron's per-user userData directory (on Windows, %APPDATA%\velora-browser\data), so it survives app auto-updates; in development it sits at the repository root.

The choice is deliberate. Plain JSON is auditable, diff-able, and trivially backed up. Append-only is a defence against history rewriting by a bug, a crash, or a malicious plugin.

6. Canon manifest

canon_manifest.json at the repo root is the integrity ledger for tracked files. Every entry is a {path, sha256} pair. The architecture_flags block records invariants that cannot regress (echo-only architecture, single AI gateway, policy layer enabled, Kids Mode locked, file-backed single source of truth, deny-by-default permissions, and others).

Automated hash audits run before and after edits. A drift between an entry's hash and the file's actual hash blocks a release until the drift is reviewed.

7. UI surface map

The Velnode UI is a small set of HTML files, each rendered into a sandboxed renderer. The chrome and the tab content live in different renderers; HTML surfaces internal to Velnode live in their own renderers:

Surface HTML file
Main chrome / tabs index.html
Dashboard dashboard.html
Settings settings.html
History history.html
Timeline view timeline.html
Sticky Memory board sticky-memory.html
Markdown viewer markdown-viewer.html
PDF viewer pdf-viewer.html
Blocked-site interstitial blocked.html

The canonical design-token vocabulary is --velora-*, shipped in tokens.css (Phase 11 M7). New CSS additions use var(--velora-*) directly; legacy --vlr-* aliases exist only for the ten pre-token CSS files.

8. Plugin runtime

The Modular Runtime Core (Phase 8) loads plugins in isolated contexts with declared permissions. Key guarantees:

The official Veehive store is the trusted distribution surface, and Velnode only loads a plugin that carries Velnode's own Ed25519 signature โ€” an unsigned, tampered, or revoked plugin is refused at load time and never executes. Today Veehive's catalogue is first-party and curated; a signed third-party developer ecosystem is planned for a future release.

9. Voice (Vee Whisper)

Voice is an opt-in surface. The base text-input path works without it. When enabled:

10. Versioning

Velnode ships under semantic versioning: MAJOR marks a breaking redesign, MINOR a shipped phase, PATCH a sub-phase or fix. The current package.json version mirrors the canon manifest semver. v1.0.0 marked the seal of Phase 8 Vee Workflows v1. Prior phases are not retroactively tagged.

The release workflow seals the canon manifest, bumps package.json, commits with a chore(release): message, tags the semver, and pushes main with --follow-tags.

11. Security defaults (summary)

Default Where enforced
nodeIntegration: false Every webContents
contextIsolation: true Every webContents
sandbox: true Every webContents
IPC allow-list preload.js and main.js
Navigation guardrails Main-process navigation hook
Deny-by-default permissions Main-process permission hook
API keys in main-process storage Main process
No remote-loaded scripts in renderer CSP + design rule
No eval Coding rule + lint

These defaults are reviewed together as a set whenever the AI, voice, or permissions path changes.

11a. Cloud account layer (Phase 21 Stage 1)

As of Phase 21 Stage 1, Velnode adds an optional cloud account layer. The local-first architecture is unchanged; the account layer is additive and opt-in.

11a.1 Design principle

Two independent trust layers with different trust properties:

11a.2 Client components

Module Role
lib/account/crypto.js Pure Node crypto. KDF, key wrapping, recovery. No I/O, no Electron. Testable in isolation.
lib/account/store.js Local persistence in data/account.json (whole file wrapped via safeStorage, mirroring the vault).
lib/account/api-client.js HTTPS transport to the Worker. No crypto, no disk.
main.js VLR.ACCOUNT.*.V1 IPC orchestration, sender-guarded, plugin-denied.
account.html / .js / .css Presentation only. Secrets are never handled in the renderer.

11a.3 KDF and key hierarchy

All KDF uses scrypt N=2^17, r=8, p=1 with domain-separation labels. Two independent derivations from the account password:

The server re-hashes auth_hash with PBKDF2-HMAC-SHA256 and a per-row salt, storing only the auth_verifier. The server never sees the password or the KEK.

A random 32-byte data_key is generated client-side, wrapped by the KEK with AES-256-GCM, and stored as an opaque blob in D1. A second copy of the data_key is wrapped by a recovery_KEK derived from the 24-word BIP-39 Recovery Kit and stored alongside the first.

11a.4 Session and at-rest caching

The bearer session token is a 32-byte random value; only its SHA-256 is stored on D1. The unwrapped data_key is cached at rest via Electron safeStorage (consistent with the vault and sync-identity pattern) so users are not prompted on every launch. It is cleared on sign-out and on lock.

11a.5 Worker topology

The velora-account Worker is a separate Cloudflare Worker from velora-sync (which handles R2 blob relay for Phase 14 M3 sync). The two Workers are independent; the existing sync relay is untouched. The velora-account Worker handles only identity, session, and blob-escrow operations.

11a.6 Invariants carried from the existing architecture


Document maintained by Dignity New Zealand Limited. Questions: [email protected].