w17ctl is the single client and entrypoint for a w17 project. Everything a
developer or a pipeline does — scaffolding, change requests, codegen, applying
migrations, running the local stack — goes through it.
w17ctl <command> --help # context-sensitive help for any command
curl -fsSL https://get.w17.dev/install.sh | sh -s -- --pre
Installs into /usr/local/bin, verifying the download against the release’s
SHA256SUMS first — always, with no flag to skip it.
--pre is needed while w17ctl is in release candidates: latest means the
newest STABLE release, and there is not one yet. Drop it once v0.1.0 ships.
curl -fsSL https://get.w17.dev/install.sh | sh -s -- --version v0.1.0-rc.1 --dir ~/bin
Or take a .tar.gz straight from Releases
— linux and macOS, amd64 and arm64. There is nothing to configure after
unpacking: w17ctl is a single static binary and keeps its state in ~/.w17/.
w17ctl version # what you have, and which console it defaults to
w17ctl login # point it at a console
w17ctl version prints the console address compiled into the binary. That is
only the LAST resort in the resolution order — --console, then the console
you are logged into, then this default — so logging in is what decides where
commands go.
w17 is built to be driven by a coding agent (Claude Code, …). Two steps:
1. Log in once:
w17ctl login
2. Paste this to your coding agent ONCE to onboard it (it’s context, not a task — the point is that the agent learns w17 and records it, so every later session already knows the drill and you can just ask for features):
This project is built with w17 (wandering-compiler): a declarative compiler where
you declare schemas, DB models, queries, mutations, events, and APIs as proto +
annotations, and it generates the gRPC binaries, DB layer, SQL migrations,
gateways, and clients — the only hand-written code is the business-handler bodies.
Do this now to learn how it works:
1. Run `w17ctl guide`. It writes AGENTS.md (how to drive the w17ctl CLI) and
w17/specs/ (the platform reference: the annotation catalog, the generation
model, the event system, the directory structure).
2. Read AGENTS.md and ALL of w17/specs/.
3. If it isn't already recorded there, note the essentials in this project's
CLAUDE.md / AGENTS.md and in your long-term memory, so future sessions start
from it: this is a w17 project driven via w17ctl; you change behaviour by
editing proto/ and running `w17ctl codegen`, NEVER by hand-editing generated
files; only business-handler bodies are hand-written; the full platform
reference is w17/specs/ (refresh with `w17ctl guide`).
From now on I'll just ask you to implement features. Always follow the w17 way:
declare then generate, hand-write only handler bodies, and re-read w17/specs/ when
unsure about an annotation or the architecture. Do not invent DB or gRPC plumbing
by hand.
That’s it — the agent learns w17 once, records it, and from then on you just describe features. The platform reference is regenerated by the compiler, so it always matches your w17 version. Everything below is reference detail you’ll rarely need to read.
w17ctl is deliberately dumb. It holds no compiler know-how. Every real decision — compiling the proto IR, planning SQL migrations, running the compatibility engine, signing the lock — happens on the console (the backend), reached over a gRPC API. w17ctl’s whole job is three things:
w17/lock.yaml,
generated code, snapshots, and the dev-machine config under ~/.w17/. you / CI ──▶ w17ctl ──▶ console gRPC API ──▶ the compiler core
(transport + (all the know-how) (private IP)
disk + docker)
◀── finished artifacts the client writes verbatim ──
So when a command “generates code” or “plans a migration”, w17ctl is uploading your inputs and applying what the console sends back — it never compiles anything itself. This is why the client is small, publishable, and safe to run in CI: it can’t produce a broken or forged artifact, only apply a console-signed one.
w17ctl finds your project by walking parent directories to the nearest
w17/lock.yaml, so you can run it from anywhere inside the tree.
| Location | What | Notes |
|---|---|---|
w17/lock.yaml |
the signed project lock — connections, targets, generated-code config, pinned schema | edited by scaffolding commands; signed by the console |
~/.w17/auth.yaml |
console credentials (bearer + org memberships) per console | written by login |
~/.w17/config.yaml |
dev-machine project registry + per-project host-port assignments + presets | written by init / project |
w17/certs/, w17/snapshots/ |
local dev PKI + branch schema snapshots | certs / db / initiative |
| Command | What it does · under the hood |
|---|---|
guide |
Write AGENTS.md (how to DRIVE w17ctl — golden rules + workflow + task→command cheat-sheet; client-embedded, works offline) and fetch w17/specs/ (how the w17 TECHNOLOGY works — the annotation catalog + architecture primer, generated server-side so it always matches the compiler). This is how a coding agent is onboarded: it reads AGENTS.md automatically, which points it at w17/specs/. --stdout prints the usage guide; --force refreshes AGENTS.md; --no-specs skips the server fetch. The w17/specs/ fetch needs a reachable console (run login first). |
Talk to a console and pick your org scope. Login is a single gRPC call to the console (the same endpoint every other command dials) — email + password in, bearer out.
| Command | What it does · under the hood |
|---|---|
login [<host>] |
Sign in over gRPC (AuthService.SignIn) and store the bearer + org memberships in ~/.w17/auth.yaml. Prompts interactively for anything not given (email → password (hidden) → host); --email + --password (or W17_PASSWORD) keep it non-interactive for CI. |
logout [<url>] |
Drops a console’s stored credential from ~/.w17/auth.yaml. |
whoami |
Prints the stored identity + orgs for the active console (--all = every logged-in console). Pure local read. |
org list / org use <slug> |
List orgs you belong to (server read) and pick the default; the default org scopes subsequent commands. |
These edit the signed lock and write starter proto files. No codegen — they prepare inputs.
| Command | What it does · under the hood |
|---|---|
init |
Wizard: prompts for project name + generated-code paths, registers the project with the console, writes a fresh signed w17/lock.yaml, and auto-registers it in ~/.w17/config.yaml. Refuses if a lock exists. |
connection <cmd> |
Wizard to add a DB/store connection to the lock (identity = connection name). Edits the signed lock. |
domain <cmd> / module <cmd> |
Scaffold the proto/domains/<domain>[/<module>]/w17.proto cascade sentinel; --with-example seeds the four-layer (queries / mutations / business / types) shape. Pure local file writes. |
template [<surface>] |
Prints a commented example .proto for one surface (domain, module, types, queries, mutations, business, rest, admin, events, subscribers, rpc, mcp, cli). A “how do I annotate X” reference; --list to enumerate. |
target <cmd> |
Declare what codegen emits + how it deploys — the lock’s generated_code entries: client (FE clients), grpc-client (Go client pkg), business, binary (composed binaries), ci, scale (prod replicas). All edit the signed lock. |
plugin <cmd> |
List embedded + installed plugins; install one (e.g. auth) into the project — stages its proto tree + records activation. |
secrets <cmd> |
Production-secrets (age tier): init mints a project age keypair; encrypt turns a plain .secrets into a committable .secrets.age. Runtime decrypts when a key is present, reads plain otherwise — seamless dev, strong devops. Local crypto only. |
certs |
Fill a directory with a local dev PKI (self-signed CA + CA-signed leaf) for the internal-mesh TLS switch (W17_INTERNAL_TLS=on). Idempotent — never overwrites. |
The compiler-driven commands. Each uploads your protos to the console and writes back what it returns.
| Command | What it does · under the hood |
|---|---|
codegen |
Generate all derived code: Storage gRPC handlers + the ACL / eventbus / MCP bundles. Uploads every .proto under proto/ to the console, which compiles the IR + emits the files; w17ctl writes them under <root>/<gen_dir>/. |
verify |
Re-checks that every committed generated lock (ACL + eventbus) still matches the current proto — the CI drift hook paired with codegen. Offline (no console); non-zero exit on drift. |
push --proto=… |
Push everything (schema + fixtures + future artifact types) to the console in one call; the server diffs + stores. Idempotent — safe to re-run whenever inputs change. |
migrate generate |
Compile + push the schema; the console plans the SQL migrations (initial create vs. revision diff, auto-detected) and pins the lock target. migrate list shows history; migrate fetch downloads the planned .up.sql; migrate push-raw is the hand-authored-body escape hatch. |
fixtures <cmd> |
Apply console-rendered fixture seeds (parameterized upserts) to a connection’s target store (DSN via W17_TARGET_<CONN> env). |
clean |
Remove codegen output (pb stubs / FE clients / languages / generated bundle files) while preserving hand-written packages. |
Snapshots + a compatibility engine + a review/merge gate. Git branch → initiative (main/master → trunk).
| Command | What it does · under the hood |
|---|---|
initiative <cmd> |
Initiatives + snapshots: current / list / show / materialize / push. A git branch maps to an initiative; the first push lazily materializes it. Schema state lives in the console’s generated storage. |
compat <cmd> |
Compatibility report between two snapshots — DB / WIRE / API findings + severity — with a dev/prod policy gate. report --base --head, or against-trunk. The console’s compat engine does the analysis; w17ctl renders the verdict. |
review <cmd> |
open / show / approve / merge a set of schema changes. Merging into a prod-mode env runs compat and blocks BREAKING changes (exit non-zero); dev warns + proceeds. |
env <cmd> |
Deployment environments + mode (dev/prod). A prod-mode env turns the compat engine’s BREAKING verdict into a hard merge block. |
Docker + local project ergonomics. This is where w17ctl drives compose.
| Command | What it does · under the hood |
|---|---|
stack <cmd> |
Local docker-compose lifecycle: build / up / down / logs / ps / restart. Injects the project’s unique host ports from ~/.w17/config.yaml. |
project <cmd> |
Dev-machine project registry: list / import / remove / ports / ps + run presets. Keeps unique host ports across all installed projects (no env-file juggling) in ~/.w17/config.yaml. |
db <cmd> |
Local dev DB snapshots scoped to the current initiative: save / list / activate / delete. Branch-scoped checkpoints for fast switching. |
test |
Run the generated e2e suite against a deployed gateway. Locates the e2erunner module and runs it in Docker against --target — a pure HTTP/MCP client that never touches a DB. |
ui <cmd> |
Serve the local web app — a human-facing view + controller over the project registry, presets, and Docker stacks (the GUI counterpart to project / stack). On-demand, 127.0.0.1 only; no daemon, no image. |
The compiler’s IP and all trust-bearing crypto (lock signing) live on the
console, never in the client. w17ctl ships as a standalone public tool and
imports zero private compiler code — only the public sdk/go module + the
console’s gRPC contracts. That’s what lets the same minimal client both scaffold
a project on your laptop and gate a production release in CI: it verifies + applies
console-signed artifacts, and can’t forge one.