Skip to content

Develop Lucidos

Point a Lucidos workspace at a Lucidos source checkout and Lucidos can work on its own code. You describe the change you want. A coding agent works in an isolated git worktree, commits there, and proposes the result as a pending change. You read the diff, then click Apply to merge that branch into main. The UI is the same in a phone browser, so you can review and Apply from a phone.

Only a checkout includes the platform source, so only a checkout can change it. To run Lucidos, use the desktop app or the one-line installer.

Lucidos is a Rust engine, a TypeScript frontend, and PostgreSQL with pgvector as the event store. You pick Claude Code or Codex as the coding agent per thread. Lucidos is MIT licensed.

What happens when you Apply

Apply merges the branch. What happens next depends on what the change touched.

  • A frontend-only change: the engine picks up the newly built client in place, and the page offers a Refresh to load it.
  • An engine change (Rust source, a migration, the SDK bundle) needs a new binary. Lucidos rebuilds in the background while the running engine keeps serving. When the new binary is ready, Lucidos offers Switch to new version. You choose when to switch, and in-flight threads resume after it.

Review and hardening

A coding-agent thread commits only inside its own throwaway worktree, so you always get a diff to read before anything merges. A change to Lucidos's own source must also pass /harden. It reviews the diff against the project's rules and runs the test suites for the layers it touched. If a session skipped it, Apply runs it synchronously and you wait.

Two ways in

Clone and run it. Clone the repository, then start a workspace with ./scripts/web-dev.sh. Pass -b the first time so it builds the engine. The script starts PostgreSQL with pgvector in Docker and runs the Rust engine natively. It also starts the shared dev gateway and serves the frontend behind a build watcher. Prerequisites and dev setup below has the prerequisites and exact commands.

Or bootstrap it in one command. On a clean machine, the installer's --dev flag does the whole setup:

curl -fsSL https://lucidos.dev/install.sh | sh -s -- --dev

--dev (alias --source, or LUCIDOS_FROM_SOURCE=1) bootstraps the toolchain (Rust, Node, Docker, build deps), clones the repo to ~/lucidos, compiles the engine from source (a release build, typically 10–20+ minutes on a clean machine; LUCIDOS_DEBUG_BUILD=1 for a faster debug build), and starts the stack via scripts/run.sh.

The build is the slowest step. Use the debug flag if you only want to get running.

Prerequisites and dev setup

Prerequisites

  • Rust (stable toolchain)
  • Docker (for PostgreSQL + pgvector)
  • Node.js (for Vite frontend dev server)
  • An LLM provider (Anthropic, OpenAI, OpenRouter, xAI, Vertex AI, or a local OpenAI-compatible endpoint such as Ollama / LM Studio / vLLM). Configure it in Settings → Models → Providers, or via the environment variables below. The same screen also offers OpenCode Free, a keyless tier that needs no account and no key. It is off by default, and the toggle explains the terms.

Dev Setup

# Start with a workspace directory
./scripts/web-dev.sh -w ~/workspaces/dev

# Build engine first if no binary exists
./scripts/web-dev.sh -w ~/workspaces/dev -b

# Write the generated files (the launchers and Vite run it for you)
./scripts/codegen.sh

# Other scripts
./scripts/stop.sh -w ~/workspaces/dev   # Graceful shutdown
./scripts/restart.sh -w <ws>   # Stop and start
./scripts/status.sh            # Check health

This starts one shared PostgreSQL Docker container, builds/runs the Rust engine natively, starts the shared workspace gateway, and runs a frontend build watcher. Each workspace gets its own engine port and its own database (lucidos_<slug>) inside the shared Postgres cluster, so multiple workspaces can run concurrently without one Postgres container per workspace.

Ports

The first workspace lands on 5173 for direct engine access: plain http://, loopback only, so it answers only on this machine (ADR 0096). Each additional workspace gets the next engine-port offset (5174, 5175, …), stored in ~/.lucidos/port-registry so the same workspace gets the same engine port every run. The shared dev gateway serves workspaces at http(s)://localhost:5251/<slug>/, and LUCIDOS_DEV_GATEWAY_PORT overrides the port. Dev uses 5251 rather than the packaged app's 5252, so a dev gateway and an installed Lucidos.app coexist. PostgreSQL uses one shared Docker container (lucidos-pg-shared) with one database per workspace; the chosen PG port is written to <workspace>/.lucidos/ports.

If the target port is already taken by something else (e.g. another Vite app on 5173), Lucidos walks forward to the next free offset and persists the new assignment. It does not kill the squatter. To pin a specific port:

  • LUCIDOS_VITE_PORT=5273 ./scripts/web-dev.sh -w dev: env var, one-shot.
  • <workspace>/lucidos.toml: per-workspace, persistent:
    [ports]
    vite = 5273
    

Env var beats lucidos.toml. Both still collision-walk forward if the chosen base is taken. The chosen ports are logged to stderr when web-dev.sh starts.

Environment Variables

Variable Default Description
LUCIDOS_WORKSPACE (none) Workspace directory
LUCIDOS_VITE_PORT 5173 Base direct engine port, overrides per-workspace offset + lucidos.toml.
LUCIDOS_DEV_GATEWAY_PORT 5251 Shared dev gateway port for /<slug>/ and /~/ (5251 keeps dev clear of the packaged app's 5252).
LUCIDOS_MODEL claude-opus-5-5@default LLM model name
VERTEX_PROJECT_ID (none) GCP project (for claude-*/gemini-*)
VERTEX_REGION europe-west1 Vertex AI region
OPENAI_API_KEY (none) OpenAI key (for gpt-* models). Fallback below a stored openai credential; if unset too, the engine auto-detects a key from the Codex CLI's ${CODEX_HOME:-~/.codex}/auth.json (apikey login only).
ANTHROPIC_API_KEY (none) Anthropic key (for direct claude-* models). Fallback below a stored anthropic credential. Always used as an API key, never as a subscription OAuth token.

Per-workspace environment variables: beyond the global .env the engine loads at startup, each workspace can define its own non-secret environment variables in Settings → System → Environment variables (DB-backed). The engine injects them as real env vars into every subprocess it spawns (run_bash, run_python, scheduled scripts, triggers, and coding-agent sessions) alongside CRED_*/OAUTH_*. Use them for per-workspace identity (e.g. GH_CONFIG_DIR / GIT_SSH_COMMAND so gh / git push authenticate as the right account). Changes take effect on the next subprocess, no restart. They are non-secret (real secrets belong in credentials). The legacy <workspace>/data/.env file is migrated into this store on startup and removed.

Where your workspace lives

A workspace is a directory you choose and pass with -w (for example ~/workspaces/dev), separate from the checkout. It holds your artifacts, apps, triggers and knowhow under data/, plus a rebuildable .lucidos/ runtime directory. One checkout can serve several workspaces at once. Each gets its own engine port and its own database in the shared PostgreSQL cluster.

What a coding-agent thread can work on

When you start a thread, the Coding agent on… group in the compose destination picker offers three kinds of target. The target you pick is the agent's default write scope. Its worktree is a throwaway checkout of that target only. Writing outside it needs your approval (see Permission cards).

Target What the worktree is How the work lands
Lucidos source A full worktree of the Lucidos repository. Offered only when the engine was launched from a source checkout. Proposed as a change; Apply merges it into main. /harden runs. An engine-affecting change then offers Switch to new version.
An installed app (data/apps/<id>/) A worktree of your workspace's git, sparse-checked-out to that one app folder. Proposed as a change with the same Apply flow, against the workspace git. No engine restart, and Lucidos's /harden does not run (apps own their own hardening).
A registered external repository A full worktree of a git repository you registered. You review the diff in the external-repo diff viewer, then push or open a pull request yourself.

The engine refuses any other target, with a message naming what to use instead:

  • A path under data/ other than one whole app folder (knowhow, triggers, artifacts, scripts) belongs to the chat path and its file tools.
  • An unregistered folder must be registered as a repository first.
  • The engine's own .lucidos/ state directory is never a target.
  • System roots are refused, unless you registered a repository that lives under one.

Permission cards

Inside its worktree, the agent writes without asking. Three things raise a permission card instead:

  • a shell command that the agent's own gate flags
  • a write outside the worktree, anywhere else on your machine
  • a write into the worktree's .git directory, whose contents do not appear in the diff you review

The thread waits until you answer. A card offers four answers:

  • Allow once
  • Allow for this thread: remembered for that thread's lifetime.
  • Always allow: remembered for every future thread, and editable under Settings → Permissions.
  • Deny

Contributing

The GitHub repository is a published mirror of the development repo. You fork and open a pull request as usual. A maintainer then imports your PR into the next release, credits you as co-author, and closes the PR with a link to that release.

  • CONTRIBUTING.md is the guide: branch and PR flow, commit conventions, which test suites to run for what you touched, and the DCO sign-off (git commit -s) required on every commit. CI does not run on pull requests, so run the relevant suites locally and say in the PR what you ran.
  • Code of Conduct applies to everyone taking part.
  • SECURITY.md has the private disclosure process. Do not open a public issue for a vulnerability.
  • GitHub Discussions is the place for questions, ideas, and anything open-ended.

Lucidos is pre-1.0. The public surfaces (events, the HTTP API, the JS SDK, the database schema, the on-disk layout) can change without notice, and main changes often.