Install

One command points a coding client at Drael. It writes that client's own configuration file and nothing else.

npx @drael/code

It asks for your key and which client, and nothing else. The host it already knows: there is one origin, https://drael.sh (ADR 0017). Both answers can be flags instead:

npx @drael/code --key dk-YOUR-KEY --client kilo

Through npx rather than curl | sh, deliberately. Piping a URL into a shell is exactly the pattern this product's own users are right to refuse, npx is already on the machine of anybody running an editor extension, and it makes Windows work without a second implementation to keep in step.

The flags

FlagWhat it does
--hostThe base URL, for a local build or your own box. Defaults to https://drael.sh, and DRAEL_HOST overrides that. It must start with http:// or https://, and trailing slashes are stripped.
--keyThe API key. It must start with dk-. Falls back to DRAEL_KEY, then to a prompt.
--clientopencode, kilo or env. There is no environment fallback; without it you are asked.
--listPrints what it knows how to configure, and exits.
--dry-runPrints exactly what would be written, and changes nothing.
--uninstallRestores every file it changed, and exits.

The key is typed masked rather than into your scrollback, the client is chosen with the arrow keys, and the one already on your machine is what the cursor starts on. That last part is a hint and never a gate: a fresh install may not have written its directory yet, so every client stays on the list.

What it prints is a ruled grid: a label column on the left, what the label names on the right, and a hairline where one part ends and the next begins. That is the page's own structure rather than a terminal convention, and it is the reason there is no box drawn around anything.

Output is dimmed and bolded, never coloured, and goes plain the moment stdout is not a terminal or NO_COLOR is set. Prompting needs a terminal: without one it says which flags to pass rather than hanging.

It makes one network call, and only one: GET /v1/models against the host it is configuring, with the key it is configuring, to write the model id that host actually serves rather than one assumed here. A host that does not answer within four seconds is not a reason to refuse to configure anything, so the published id stands in and the write goes ahead.

That call is not a check. It does not verify that the host is reachable or that the key is valid: the last thing it prints is the curl that checks both, for you to run yourself.

What it touches

ClientFile
opencode~/.config/opencode/opencode.json
kilo~/.config/kilo/kilo.jsonc, Kilo Code
env~/.config/drael/env.sh, for anything reading OPENAI_BASE_URL

All three are written with the mode 0600, because a file holding an API key is not a file other accounts on the machine should read. XDG_CONFIG_HOME is honoured where it is set, and on Windows the equivalent under %APPDATA% is used. kilo is the exception on both counts: Kilo documents ~/.config/kilo/kilo.jsonc literally, on Windows too, so that is where it is written.

The env file exports OPENAI_BASE_URL, OPENAI_API_KEY and OPENAI_API_BASE, the last because some clients still read the older name. Nothing outside those paths is touched, and no shell profile is edited without you doing it: the env mode prints the one line to add and leaves the decision to you.

The opencode file is written whole rather than merged, which is why the backup below exists.

The kilo file is merged, because it is the whole extension's configuration and not ours: the other providers, the model and everything else in it are kept, and only the drael provider is added. A file that does not parse as JSON is replaced instead of merged, and it says so before it does: JSONC allows comments, and a parser guessing at them would write back a mangled config.

Undoing it

npx @drael/code --uninstall

Every file it wrote is backed up before it is written, under ~/.local/state/drael/installer. Uninstall restores the originals byte for byte, and removes the files that did not exist before. It restores everything the installer has ever recorded on this machine rather than one client's file, so it is an undo of the tool and not of the last run.

Installing twice keeps the first record: what an uninstall restores is the state before this installer ever touched the file, not what the previous run left. The first version got this wrong, recorded two conflicting states, and the removal won over the restore.

Before you run it

--dry-run prints exactly what would be written and changes nothing, including the backup.

The source is one file in the repository, frontend/packages/installer/src/code.js, and this page points at it on purpose. It is short enough to read in a sitting, and its one dependency is @clack/core, which reads raw mode, tracks the keys and carries the cancel: five packages and 272K, against the twenty-three megabytes a React-based terminal UI would have cost something that runs through npx. The drawing is not its: @clack/prompts sits on top of the same core and draws a rail and coloured symbols, which are its identity rather than this one's, so the frames here are written against the design system instead. The people most right to distrust an opaque installer are this product's own users, so it earns the trust by being readable rather than by asking.

Which clients

Three today. A client is added when real usage justifies it, not pre-emptively: each one is a configuration format that can change under us, and a client nobody here runs is a client we would break silently.

The env mode covers everything that reads the OpenAI environment variables, which is most CLIs and most libraries.

Doing it by hand

There is nothing privileged in what the installer writes. Any OpenAI client takes the same two values directly:

export OPENAI_BASE_URL="https://YOUR-HOST/v1"
export OPENAI_API_KEY="dk-YOUR-KEY"

The model ids are drael-v1 and drael-v1-lite, and GET /v1/models publishes the ones your key may use. See quickstart.