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
.gitdirectory, 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.