Skip to content

Repository files navigation

Basalt

This repository is the build system: everything needed to configure, generate, and run a Basalt deployment. What Basalt is — the architecture and the services — is described in BASALT.md. This document is the operating manual: starting the stack, stopping it, and connecting your people and your agents.

Basalt is the Genworks-maintained fork of Basilisk.

A standard deployment runs an interactive console (Emacs, with MCP and HTTP endpoints), two engine services (Gendl on CCL and on SBCL — Common Lisp computation and geometry, reachable over HTTP, MCP, and SLIME/Swank), and a monitor (autoheal). Users and agents address any service by its hostname. The whole set is declared in one file, basalt.sexp, and every other configuration file is generated from it.

Starting a deployment

Requirements: Git and Docker.

git clone https://github.com/genworks/basalt
cd basalt
./basalt up

Images are pulled as needed, services start and are health-validated one by one, and the deployment comes up under a generated name — assigned on first start, kept in .ship, and used as the name of the Docker network all services join.

If you have a ~/projects/ directory it is bind-mounted at /projects in every container, and created if missing. To mount a different directory (never edit .env by hand — it is generated by ./generate-env.sh):

PROJECTS_DIR=/path/to/projects ./basalt up

A first up also makes a few deliberate changes on the host:

  • eskew and egskew become shell commands — they connect you to the console (see Connecting, below).
  • Placeholder credential files are created under ~/.claude/, ~/.gemini/, ~/.codex/ and ~/.grok/ where missing, so the console's file mounts resolve cleanly.
  • If a ~/.claude.json already routes MCP through some mcp/mcp-exec, it is conservatively re-pointed at this clone's copy (a backup is kept; nothing else in the file is touched).

Commands

./basalt <command>
command what it does
up [service...] start the deployment, or named services only; --pull fetches fresh images
down stop and remove all services
restart [service...] restart one service, or all
stop [service...] stop without removing containers
status, ps container status
logs [service] follow a service's logs
emacs attach an emacsclient terminal to the console
pull pull missing images (PULL_ALWAYS=1 for latest)
config show the merged compose configuration
install-shell-functions (re)write the eskew/egskew helpers
clean remove containers, networks, and images — asks first

Variant switches (--lite, --default, --tui, --gui, --full) pick the console image variant for the invocation; the dev stack defaults to full.

For a deployment that should survive host reboots unattended, hand it to systemd:

cd systemd && sudo ./install
sudo systemctl start basilisk     # likewise stop, restart

Updating

Stop the deployment before pulling the repository, in case the pull changes files a running stack would refuse to stop cleanly against:

cd ~/projects/basalt
./basalt down
git pull
./basalt up --pull

Several deployments on one host

BASILISK_INSTANCE=alpha BASILISK_PORT_OFFSET=100 ./basalt up

Each instance gets its own generated name, network, and ports. Within any instance the services keep their canonical hostnames — captain resolves to that instance's console — so nothing configured against a service name needs to know which instance it runs in. To reach a second instance's console from the host, prefix the command: BASILISK_PREFIX=alpha- eskew.

Connecting

Ways in for a human user:

way in how
eskew the console, in your current terminal
egskew the console in a graphical frame (falls back to the terminal without a display)
the web terminal a terminal in your browser: http://localhost:6942
./basalt emacs the console, without the shell helpers

Detach from eskew/egskew with ctrl-^.

MCP clients

./basalt up generates client registry files into mcp/, detecting the platform as it goes:

file for
mcp/claude_desktop_config.json Claude Desktop on this or another machine — walkthrough in docs/CLAUDE_DESKTOP.md
mcp/claude-code-mcp.json Claude Code on the host — mcp/install-claude-code-config splices it into ~/.claude.json
mcp/mcp.toml, mcp/mcp-container.json, mcp/mcp-windows.json other clients, and the agents inside the console

Agents in the console. The full console variant ships terminal agents — Claude Code, Gemini CLI, Codex, Grok — pre-configured with the same registries, so an agent running at the console reaches the same services an outside client would. The lite variant carries none; outside clients work identically either way. Which agents, and where each keeps its configuration, are documented with the image: see skewed-emacs/docker/README.md.

Every entry in these registries connects directly to the service it names, with no intermediary gate: treat issuing them as issuing an invitation. An authenticating front door for uninvited clients is a supported pattern that the standard deployment does not configure.

Once a client is connected, mcp/opening-prompt.md is a suitable ready-made first message.

The configuration, and the generated files

The deployment's configuration is basalt.sexp — the one file written by hand. Its companion glossary.sexp defines the configuration vocabulary and the display strings; editing either requires no change to any shipped code. The generator reads the configuration file named for its own directory and no other — configuration written for a differently-named build system is not read, and the error names both files.

After editing the configuration, regenerate:

(load-file "/projects/basalt/generate-configs.el")
(skewed-generate-configs "/projects/basalt/")

This is commonly run from the console of an already-running deployment, in preparation for starting another.

Generated outputs include, among others:

generated file what it is
docker-compose.yml the compose configuration the deployment runs from
mcp/mcp*.json, mcp/mcp.toml MCP client registries
generated/services-generated.el the console's copy of the service inventory
generated/crew.env the service ledger: each role, and the service filling it
generated/vocabulary.env display strings for startup and status output

The generated files are outputs. Never edit them directly; edit the configuration and regenerate.

Adding services

Everything beyond the standard set — licensed engine variants, an ingress (reverse proxy), a monitoring dashboard — arrives by stack repository: a repository beside this one, one per deployment design, carrying its own additional configuration. Stack configuration is sparse: it states only what deviates from or adds to the base, and inherits the rest.

mkdir ~/projects/my-stack && cd ~/projects/my-stack
$EDITOR basalt.sexp            # your deviations and additions

A stack repository may be named anything; its configuration file is always named for the build system it feeds — hence basalt.sexp here.

Generate the stack's output files, then install them and start:

(load-file "/projects/basalt/generate-configs.el")
(skewed-generate-configs "/projects/my-stack/")
./install                      # copies the outputs into ../basalt/
cd ../basalt && ./basalt up

Set BASILISK_DIR= if your Basalt clone is not the sibling directory. Re-run both the generation and ./install after every configuration edit.

Configuration that must name other services — an ingress's routing rules, a services-init hook — names a role (${BASILISK_POST_CAPTAIN} and kin), resolved against the service ledger at start time, so the rules survive service renames.

Bringing your own image is not a special case: a stack repository whose configuration adds one more service. It joins the deployment's network and resolves by name from every container. An image deployed with no role is entered on the roster as unassigned (unassigned-<repo>) — running and monitored, assigned to nothing.

A configuration of your own

A stack repository can only add. For a deployment that runs only what you declare, fork this repository, rename it for your own system, name the configuration file to match, and cut the service list to taste. The generator derives everything from the directory name, so the rename requires no code changes.

Startup will warn and proceed for every standard role you omit — a console is recommended, not required, and a deployment without one is a standalone service group rather than an interactive one.

License

Copyright © 2026 Gornskew Enterprises. Basalt modifications copyright © 2026 Genworks International.

Basalt is free software, released under the GNU Affero General Public License, version 3 or later — the full text is in LICENSE. The images a deployment pulls carry their own terms, stated at their own registries.

Origin of record

The origin of record for this repository is maintained by Genworks International, and mirrored to github.com/genworks/basalt. Public issues and pull requests may be lodged against the github mirror.

About

Genworks Basalt Workstation

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages