Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
262 commits
Select commit Hold shift + click to select a range
185e0ea
CLI: output contract and config packages join the root module
ThallesP Oct 8, 2026
3895cfd
Go: serve wires jobs and realtime, recovery pass, graceful shutdown; …
ThallesP Oct 8, 2026
9c83fae
auth: domain rules, device flow decisions, password hashing (argon2id…
ThallesP Oct 8, 2026
7392e6a
Go canvas: projects, environments, nodes, variables use cases and SQLite
ThallesP Oct 8, 2026
1419a2f
Go deploy: deployments, apply, observe, reconcile, events use cases a…
ThallesP Oct 8, 2026
94856c6
Ingress: expose, unexpose, proxy sync and the Caddy config builder
ThallesP Oct 8, 2026
e3912ae
Go: one image for serve, proxy and agent; Makefile
ThallesP Oct 8, 2026
1e51065
Go canvas: HTTP routes and wire types
ThallesP Oct 8, 2026
3d12c55
Ingress: sync, cert report, resync and start-up tests
ThallesP Oct 8, 2026
50cca6d
Go deploy: Swarm adapter, deployment routes, POST /worker/events
ThallesP Oct 8, 2026
fb588c8
Ingress HTTP: expose, unexpose, control plane, POST /proxy/events
ThallesP Oct 8, 2026
56daf15
Go: Dockerfile builds with the legacy builder too; go.mod ignores nod…
ThallesP Oct 8, 2026
139429f
agent: tests for parsing, resume points, delivery, back pressure, rou…
ThallesP Oct 8, 2026
1f55389
observability: domain types, ports, SQLite adapter and use cases
ThallesP Oct 8, 2026
147f728
Install, deploy and CI for the single keel binary
ThallesP Oct 8, 2026
8a6048d
Ingress: keel-proxy admin client over the unix socket
ThallesP Oct 8, 2026
89abd1b
agent: end-to-end Run test, env config, moby adapter queries and read…
ThallesP Oct 8, 2026
9f3135b
Go: Dockerfile: no turbo telemetry during prune
ThallesP Oct 8, 2026
99a0bb0
auth: sessions, sign-up/in/out, organizations, invitations, device lo…
ThallesP Oct 8, 2026
22f66e6
observability: Axiom adapter, Docker log reader, API types and routes
ThallesP Oct 8, 2026
5dffaf8
Go canvas: domain table tests and use-case tests on SQLite
ThallesP Oct 8, 2026
69d4831
Go deploy: tests for summarize, settle, reconcile, Ship rules, apply,…
ThallesP Oct 8, 2026
485e757
mesh: optional embedded Tailscale (tsnet) for the agent's control-pla…
ThallesP Oct 8, 2026
0e891d5
Go deploy: Swarm adapter tests (toSpec golden JSON, agent spec, Docke…
ThallesP Oct 8, 2026
ce6c81f
Go canvas: NodeView table tests and HTTP tests
ThallesP Oct 8, 2026
98af399
auth: use-case tests (sign-up rules, sessions, invitations, device fl…
ThallesP Oct 8, 2026
002627f
keel proxy: the edge from the same binary
ThallesP Oct 8, 2026
b58b5a1
Go deploy: transport tests (worker events auth, limits and parsing; d…
ThallesP Oct 8, 2026
21f095f
agent: KEEL_CONFIG_POLL_MS accepts fractional ms like Number(), garba…
ThallesP Oct 8, 2026
3dd6204
keel proxy: build tag keel_noproxy leaves the edge out
ThallesP Oct 8, 2026
1fb2e33
Go canvas: adapter test; canvas-prefixed helper names
ThallesP Oct 8, 2026
ea7e3c1
Ingress start-up: never move a default domain onto a held name
ThallesP Oct 8, 2026
0fcadb2
Ingress: area-specific helper names
ThallesP Oct 8, 2026
eb8ff91
auth: HTTP tests (cookies, CSRF, renewal, rate limit, device flow); x…
ThallesP Oct 8, 2026
1037f74
Go deploy: remove the Quick Tunnel services at start, guard Docker jo…
ThallesP Oct 8, 2026
342db30
install.sh: move a Convex install's data before keel serve first starts
ThallesP Oct 8, 2026
7cec17c
observability: golden tests against the TypeScript providers
ThallesP Oct 8, 2026
a372a9d
Merge branch 'go/install' into go-single-binary
ThallesP Oct 8, 2026
eac6766
auth: sign-out test sends the CLI's {} body
ThallesP Oct 8, 2026
07ed9d3
Go deploy: concurrent test of the per-node apply queue
ThallesP Oct 8, 2026
fe4f9e8
observability: use-case tests (tracing switch, sign-in, relay, access)
ThallesP Oct 8, 2026
f000de9
observability: gofmt
ThallesP Oct 8, 2026
98e3bde
observability: adapter and HTTP tests; reject a non-finite around moment
ThallesP Oct 8, 2026
023690e
observability: keep cancellations unwrapped; rename a shadowing variable
ThallesP Oct 8, 2026
927695a
observability: area-prefixed helper names (merge safety)
ThallesP Oct 8, 2026
531e7a9
observability: prefix raw-route helpers
ThallesP Oct 8, 2026
45c90b5
observability: simplify the DCR client save
ThallesP Oct 8, 2026
0095a9c
Ingress sync: coalesce requests that arrive while a sync runs
ThallesP Oct 8, 2026
49175d4
auth review: a bearer request acts with its bearer, never the cookie;…
ThallesP Oct 8, 2026
2849b77
auth review: bound concurrent password hashing
ThallesP Oct 8, 2026
ce36a6f
auth review: a new keel login no longer sweeps codes that just expired
ThallesP Oct 8, 2026
0299e20
Go: platform review fixes (shutdown budget, WS cookie, Keel-Invalidat…
ThallesP Oct 8, 2026
01dca95
Go canvas review: Environment not found is PROJECT_NOT_FOUND; Redis p…
ThallesP Oct 8, 2026
cfe02f2
keel proxy: come up with the admin socket when the last config no lon…
ThallesP Oct 8, 2026
d067952
agent: default KEEL_STATE on the mounted state volume (keel-agent, el…
ThallesP Oct 8, 2026
f5f7ea3
Go deploy review: shutdown keeps applies for recovery, no double appl…
ThallesP Oct 8, 2026
fb4c57d
agent: a reconcile keeps containers that started while its list was i…
ThallesP Oct 8, 2026
61a8ad7
Go deploy review: bound the Docker calls of scans and removals
ThallesP Oct 8, 2026
a124cd9
Merge branch 'go/platform-review' into go-single-binary
ThallesP Oct 8, 2026
ae10f57
Merge branch 'go/auth-review' into go-single-binary
ThallesP Oct 8, 2026
cfc8fd6
Merge branch 'go/ingress-review' into go-single-binary
ThallesP Oct 8, 2026
2e3f2d0
Merge branch 'go/canvas-review' into go-single-binary
ThallesP Oct 8, 2026
4dc0c14
agent: a line waiting for room is routed again when its sink's queue …
ThallesP Oct 8, 2026
1b13980
Merge branch 'go/deploy-review' into go-single-binary
ThallesP Oct 8, 2026
7f2f9a6
agent: shutting down mid-POST does not move the events resume point p…
ThallesP Oct 8, 2026
45d847f
Merge fix: Recover keeps containing each area's panics
ThallesP Oct 8, 2026
278549f
agent: test the read-only guard through the constructor the agent run…
ThallesP Oct 8, 2026
dad573a
Merge branch 'go/agent-review' into go-single-binary
ThallesP Oct 8, 2026
e1046a4
Merge branch 'go/observability-review' into go-single-binary
ThallesP Oct 8, 2026
35715d6
Integration: wire every adapter in keel serve, close sockets on sign-out
ThallesP Oct 8, 2026
879226f
Integration: environment changes refresh node views; machine routes s…
ThallesP Oct 8, 2026
2ed45d9
openapi.json: the API the dashboard and CLI are generated against
ThallesP Oct 8, 2026
0f4254b
CI: fail when openapi.json drifts from the handlers
ThallesP Oct 8, 2026
23aa805
Swarm integration test: keel serve and keel agent against a real Swarm
ThallesP Oct 8, 2026
feb8a1f
CI: put the swarm job where it belongs
ThallesP Oct 8, 2026
b4e6a25
web: Kubb-generated API client, TanStack Query, realtime socket and s…
ThallesP Oct 8, 2026
27fe2d3
CLI: the keel verbs move into the binary and talk to the Go API
ThallesP Oct 8, 2026
3f80399
docs/cli.md and CLAUDE.md: the CLI over the Go API
ThallesP Oct 8, 2026
551f4cf
Design docs: name the Go code for every mechanic that moved off Convex
ThallesP Oct 8, 2026
3bb3291
Merge branch 'go/docs' into go-single-binary
ThallesP Oct 8, 2026
c5fe829
keel-agent gets the worker token as a Swarm secret, not service env
ThallesP Oct 8, 2026
70d43f5
web: Vite proxies the API to keel serve; image builds the client from…
ThallesP Oct 8, 2026
20a1d60
web: drop the temporary type-check config
ThallesP Oct 8, 2026
c1c48cf
CLI: verbs and server commands in their own help groups; stale comments
ThallesP Oct 8, 2026
c6846d5
web: sign-in, sign-up, invites and device approval over the Go API
ThallesP Oct 8, 2026
dff50d5
web: node panel, Settings page and agent prompt read the Go API
ThallesP Oct 8, 2026
56c2c37
web/canvas: canvas, topbar, project switcher and project page over th…
ThallesP Oct 8, 2026
8d6fd3c
web: Observability and the Axiom sign-in over the Go API
ThallesP Oct 8, 2026
0c3d5ce
CLI review: odd deployment ids are DEPLOYMENT_NOT_FOUND; keel builds …
ThallesP Oct 8, 2026
b844f56
CLI review: a URL that isn't Keel's API is DISCOVERY_FAILED for every…
ThallesP Oct 8, 2026
a45df8a
web/canvas: a created node keeps its deployment link when it arrives …
ThallesP Oct 8, 2026
87f9ce5
Merge branch 'go/cli-review' into go-single-binary
ThallesP Oct 8, 2026
db87d8a
Merge branch 'go/web-canvas' into go-single-binary
ThallesP Oct 8, 2026
2656161
Merge branch 'go/web-panel' into go-single-binary
ThallesP Oct 8, 2026
eee3e58
Merge branch 'go/web-obsauth' into go-single-binary
ThallesP Oct 8, 2026
1f4a4f8
Deployments settle without keel agent; keel proxy builds on Linux only
ThallesP Oct 8, 2026
28aa361
Remove the Convex backend, the Bun worker and the keel-proxy module
ThallesP Oct 8, 2026
98235e7
Address review: upgrade, auth limits, Origin check, writer safety, ap…
ThallesP Oct 8, 2026
0872368
install.sh: an empty Convex import is not a pending one
ThallesP Oct 8, 2026
d1b136b
Lint the review rules: no new comments, destructuring, effects
ThallesP Oct 9, 2026
4be26ba
Arrays are never null on the wire; drop the guards that assumed they …
ThallesP Oct 9, 2026
ca05e05
Variables: the API suggests references; no in checks; suspense in the…
ThallesP Oct 9, 2026
bb1130e
CI: run type-aware oxlint after check-types generates the API client
ThallesP Oct 9, 2026
931386b
Send span attributes as objects and drop src/api/types.ts
ThallesP Oct 9, 2026
c39726c
Replace the nested ternaries and lint against new ones
ThallesP Oct 9, 2026
50547c7
Split the Observability charts and trace view into one component per …
ThallesP Oct 9, 2026
1254062
dashboard: mutation isPending, select reads, drop dead code and renam…
ThallesP Oct 10, 2026
a978772
dashboard: keep the data-layer README in step with lib/session
ThallesP Oct 10, 2026
a796fb6
domain + api: drop dead and duplicate helpers, trust DeriveStatus in …
ThallesP Oct 10, 2026
249022d
app: auth, orgs, projects, platform: drop dead helpers, reuse what ex…
ThallesP Oct 10, 2026
b8f060a
app: canvas and deploy: drop duplicate helpers, aliases and dead code
ThallesP Oct 10, 2026
7e22b0b
app: canvas and deploy: keep apply's re-check a plain load and guard
ThallesP Oct 10, 2026
fef74e4
app: ingress and observability: stdlib over hand-rolled helpers, flat…
ThallesP Oct 10, 2026
851bf5b
adapters: drop dead getters, defensive defaults and roundabout checks
ThallesP Oct 10, 2026
6e13029
transport, serve, cmd: reuse domain JS helpers, drop aliases and dead…
ThallesP Oct 10, 2026
2e78f45
domain: keep the invalid-byte UTF-16 length case the transport test c…
ThallesP Oct 10, 2026
6bbab48
agent and proxy: inline one-use consts and temporaries, drop dead guards
ThallesP Oct 10, 2026
57112eb
CLI: drop dead and duplicated helpers, flatten a few branches
ThallesP Oct 10, 2026
6e21425
Drop the build's Owner notes and banners; one JS whitespace helper
ThallesP Oct 10, 2026
e3e0ea6
dashboard: drop dead exports and unnecessary canvas assertions
ThallesP Oct 10, 2026
530cc66
sqlite: inline the single-use variable conversion
ThallesP Oct 10, 2026
052b401
cli: inline the HTTP client fallback
ThallesP Oct 10, 2026
213afdd
Generated code gets its own space: internal/gen and apps/web/src/gen
ThallesP Oct 10, 2026
0d8c4c1
No comments in Go: strip them all and enforce it in CI
ThallesP Oct 10, 2026
32412f1
Go: drop the Convex migration and the backfills
ThallesP Oct 10, 2026
0f69e5b
Go: drop the Convex migration and the backfills: review fixes
ThallesP Oct 10, 2026
155565d
install: drop the Convex upgrade path
ThallesP Oct 10, 2026
9e24228
install: drop the Convex upgrade path: review fixes
ThallesP Oct 10, 2026
0856f8f
docs: no Convex migration, no backfills
ThallesP Oct 10, 2026
a7ca9fa
docs: no Convex migration, no backfills: review fixes
ThallesP Oct 10, 2026
8a2e97f
Drop /config.js: nothing reads it since the Convex URLs went
ThallesP Oct 10, 2026
af267da
Observability: typed Axiom rows instead of JavaScript emulation
ThallesP Oct 10, 2026
78f7581
Observability: review fixes for the typed Axiom rows
ThallesP Oct 10, 2026
566f344
jobs: drop timer bookkeeping and the test-only Pending
ThallesP Oct 10, 2026
53057a8
realtime: find a session's sockets by centrifuge label
ThallesP Oct 10, 2026
6205664
serve: wire adapters before realtime and inline one-use helpers
ThallesP Oct 10, 2026
2b7e454
serve: decode the Swarm IT test's responses into api types
ThallesP Oct 10, 2026
e0bb79e
mesh: fold the tailnet join into Open
ThallesP Oct 10, 2026
bec12ac
app: keep the tracing agent prompt in an embedded markdown file
ThallesP Oct 10, 2026
6ec2a1f
sqlite, nocomments: drop a nil fallback and two one-use helpers
ThallesP Oct 10, 2026
4f02e84
jobs: start no Every run once Stop has begun
ThallesP Oct 10, 2026
49109fa
realtime: take the actor the HTTP edge already resolved
ThallesP Oct 10, 2026
71110ab
serve: run the recovery pass as a job and drop the SITE_URL fallback
ThallesP Oct 10, 2026
8077950
mesh, web, app, nocomments: drop defaults and errors that cannot happen
ThallesP Oct 10, 2026
e736f8c
domain: single-value roles, untagged auth types, inline one-use messages
ThallesP Oct 10, 2026
a2eb83c
auth: device login decides on loaded codes, not on nil pointers
ThallesP Oct 10, 2026
4542cdb
app: auth limiter windows are durations
ThallesP Oct 10, 2026
d9aa1cc
http: typed auth responses in tests, authNullable without guards
ThallesP Oct 10, 2026
8c77709
app: inline the single-use device poll limit
ThallesP Oct 10, 2026
fcbdca4
auth: device login trusts its serialized write transaction
ThallesP Oct 10, 2026
abd76c7
http: getMe builds views with new, auth tests share an invite helper
ThallesP Oct 10, 2026
aa368b5
domain: plain Go for canvas references, names and numbers
ThallesP Oct 10, 2026
f839ea0
canvas: drop JS habits from variables and their HTTP mapping
ThallesP Oct 10, 2026
3a30560
sqlite: new(expr) for node params, cmp.Or for endpoint ids
ThallesP Oct 10, 2026
f948a87
domain: zero public port when missing, seed rows and ports named once
ThallesP Oct 10, 2026
19b5fd7
canvas: let SQLite own variable keys, typed UNIQUE check, one cluster…
ThallesP Oct 10, 2026
f118fe6
api: typed node type and engine on requests, nodes test on api.NodeList
ThallesP Oct 10, 2026
e15e1ac
http: decode worker events into typed Docker events
ThallesP Oct 10, 2026
0a69317
deploy: drop one-use helpers, constants and dead json tags
ThallesP Oct 10, 2026
06b0714
deploy: drop the never-set Sha and skip the log of running deployments
ThallesP Oct 10, 2026
b236066
deploy: flatten the ship verb and drop JS-era defaults
ThallesP Oct 10, 2026
507b75a
domain: drop unused JSON tags from node types, flatten status rules
ThallesP Oct 10, 2026
0de2b88
app: plainer node use cases
ThallesP Oct 10, 2026
ff9576c
observe: Go semantics for task revisions, one pass over tasks
ThallesP Oct 10, 2026
fc60512
invalidations: a topic list per organization, sorted on read
ThallesP Oct 10, 2026
413652f
events: one observeServers per batch of node events
ThallesP Oct 10, 2026
c6e3230
nodes: write a node once per update
ThallesP Oct 10, 2026
06470be
observe: the deploying step is a domain rule
ThallesP Oct 10, 2026
0b54688
swarm: plainer service spec and log reader
ThallesP Oct 10, 2026
ae80b0b
ingress: plain Go in expose, proxy sync and the domain rules
ThallesP Oct 10, 2026
325d7c3
ingress: typed proxy reports and a leaner caddy admin client
ThallesP Oct 10, 2026
3a55aa3
proxy: build the base config from caddy.Config and drop helpers
ThallesP Oct 10, 2026
d9b2e3d
ingress: typed Caddy config, nil-safe endpoints, one cert row
ThallesP Oct 10, 2026
ce0f983
ingress: Go text answers on /proxy/events
ThallesP Oct 10, 2026
f734903
proxy: unexport package-local names and share test setup
ThallesP Oct 10, 2026
8f3b336
agent: drop the JavaScript emulation and key sinks by their config
ThallesP Oct 10, 2026
7a0f722
agent: one exit log per event stream, typed _time, Logf
ThallesP Oct 10, 2026
dc61d60
cli: one service lookup, no client alias layer, stdlib over hand-rolled
ThallesP Oct 10, 2026
fa5762d
cli: one project lookup, errors.As, int64 epoch ms
ThallesP Oct 10, 2026
31de4c3
Integrate the idiom slices with the typed Axiom rows
ThallesP Oct 10, 2026
c493e8a
swarm: inline one-use agent constants, typed update options, no image…
ThallesP Oct 10, 2026
178ee31
app: runtime state on App, plainer deploy loop
ThallesP Oct 10, 2026
a6f5d83
swarm: trust the secret name filter when removing stale agent tokens
ThallesP Oct 10, 2026
8d36a0d
app: drop impossible branches from the deploy loop
ThallesP Oct 10, 2026
e7bf5c4
auth: plain Go for accounts, device login, passwords and the session …
ThallesP Oct 10, 2026
735c9d0
auth: typed roles, the actor's own membership, one problem builder
ThallesP Oct 10, 2026
f932b9b
http: one worker bearer check and one text writer for the raw routes
ThallesP Oct 10, 2026
9afe997
observability: worker config is one entry per sink, no project id
ThallesP Oct 10, 2026
5d1ddb5
observability: drop port leftovers from the logs, traces and sinks us…
ThallesP Oct 10, 2026
ce8e35b
http: parse the bearer once, one body limit for the raw routes
ThallesP Oct 10, 2026
eb1ca69
observability: the sink and epoch ms go straight to the Axiom queries
ThallesP Oct 10, 2026
be0b9e9
agent: slog, typed event arrays, one mesh.Open
ThallesP Oct 10, 2026
8ad1d5e
agent: sinks keyed by destination, state written outside its lock
ThallesP Oct 10, 2026
5a926f2
proxy: one admin socket default, caddy.DefaultSocket
ThallesP Oct 10, 2026
4f4f799
cli: typed API errors and device replies, no client alias layer
ThallesP Oct 10, 2026
6d046d2
cli client: name the URL when an instance URL does not parse
ThallesP Oct 10, 2026
8d61a0a
cli: Discover returns only an error, flat loginTarget, one URL parse
ThallesP Oct 10, 2026
6e0ec1a
cli: print the typed organization role
ThallesP Oct 10, 2026
8fd9248
domain: drop JS string helpers, CodeOf, ValidPort and pointer helpers
ThallesP Oct 10, 2026
8583939
sqlite: drop the settings table and its queries
ThallesP Oct 10, 2026
939d284
sqlite: InsertNode and UpdateNode report taken names, no observed nod…
ThallesP Oct 10, 2026
a3c8764
domain: plain ints for the desired port, one-shot results and deploye…
ThallesP Oct 10, 2026
66cb565
app: StopNode returns the deployment id alone
ThallesP Oct 10, 2026
5a69143
domain: database and cache defaults name their engine
ThallesP Oct 10, 2026
a9b91a1
api: NodeViewOf takes the deploying step from domain.DeployingStep
ThallesP Oct 10, 2026
b401f29
app: markReferrersDirty leaves the canvas touch to its callers
ThallesP Oct 10, 2026
a28334f
app: scopes carry the project, not a copy of its organization id
ThallesP Oct 10, 2026
ccf3489
canvas: SQLite lists production environments first
ThallesP Oct 10, 2026
354cb95
app: Conns defaults to a no-op like Events, no limits() getter
ThallesP Oct 10, 2026
4a5dbae
http: OpenAPI builds its App through app.New
ThallesP Oct 10, 2026
cb0ed7b
deploy: steps always name their deployment and its organization
ThallesP Oct 10, 2026
9032ab5
deploy: tests count proxy:sync jobs instead of a ProxySync seam
ThallesP Oct 10, 2026
5e7ce60
docs: the use case example reads the project's organization
ThallesP Oct 10, 2026
b715133
swarm: typed revision and node id on observed services and tasks
ThallesP Oct 10, 2026
b7f6cbf
events: drop the unread Docker event action
ThallesP Oct 10, 2026
86cd154
logs: the swarm adapter returns domain log replicas
ThallesP Oct 10, 2026
770224b
app: one CompactText helper for vendor details and deploy errors
ThallesP Oct 10, 2026
031f843
observability: one Axiom base URL rule in domain
ThallesP Oct 10, 2026
cc1cf20
serve: Axiom URL overrides are trimmed and gated once, at boot
ThallesP Oct 10, 2026
68ea8f2
api: drop the observability aliases, typed traces state
ThallesP Oct 10, 2026
29b01ef
cli: main passes the dashboard FS into Execute
ThallesP Oct 10, 2026
90f9166
cli config: a null instances or links map loads as empty
ThallesP Oct 10, 2026
2f19efe
agent: help and the Swarm IT no longer mention DOCKER_SOCKET
ThallesP Oct 10, 2026
e00d805
api: integers on the wire are ints, not JS numbers
ThallesP Oct 10, 2026
9f813c3
web: password forms count code points like the server
ThallesP Oct 10, 2026
ad2e4b2
docs: architecture describes the Go code as it is now
ThallesP Oct 10, 2026
9d20df3
docs: logs and workers describe the current agent and worker routes
ThallesP Oct 10, 2026
99c889f
docs: cli layout and unexpose validation match the code
ThallesP Oct 10, 2026
a717357
docs: porting specs note what the Go code does now
ThallesP Oct 10, 2026
1434549
auth: anonymous device-code lookups use the read pool again
ThallesP Oct 10, 2026
05de6d4
observability: pending Axiom orgs is an empty list, not null
ThallesP Oct 10, 2026
34f8e5c
cli: tracing enable/disable name a non-service again
ThallesP Oct 10, 2026
0d8aa1d
api: deployment, step and node statuses carry their domain types
ThallesP Oct 10, 2026
0701f0b
deploy: superseded steps go through apply's own step closure
ThallesP Oct 10, 2026
405589e
canvas: a canvas write publishes the environment topics only
ThallesP Oct 10, 2026
dfeaa63
deploy: apply uses computeEnv and followPort directly
ThallesP Oct 10, 2026
b6df114
canvas: shipping is an App field, schedulers are plain calls
ThallesP Oct 10, 2026
996e5fd
domain: one TruncateRunes
ThallesP Oct 10, 2026
f5097e2
ingress: an endpoint's domain, public port and error are zero when unset
ThallesP Oct 10, 2026
c041bb0
deploy: drop the deployments.sha column
ThallesP Oct 10, 2026
0dbc839
observability: the org's sink is a value and an ok, not a pointer
ThallesP Oct 10, 2026
554fcc1
agent: build the control plane client inline, keep only the poll
ThallesP Oct 10, 2026
737ba29
canvas: NextPosition tracks the rightmost node with a value
ThallesP Oct 10, 2026
d425634
web: the first-visit bootstrap no longer expects to found an org
ThallesP Oct 10, 2026
87b5295
ci: the CLI step's note says sign-up founds the organization
ThallesP Oct 10, 2026
7824939
deploy: step, deployment and ship times are zero when unset
ThallesP Oct 10, 2026
2ad954f
sqlite: optional text columns without a foreign key default to ''
ThallesP Oct 10, 2026
545240f
app: no net/http import; proxy env carries only what keel proxy reads
ThallesP Oct 10, 2026
532490c
Review fixes: prefixed row names, parallel variables reads, tab reset
ThallesP Oct 10, 2026
766227c
install: stop on a server that runs the Convex-era Keel
ThallesP Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
76 changes: 76 additions & 0 deletions .comments-baseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
12 apps/web/kubb.config.ts
5 apps/web/src/components/auth-forms.tsx
14 apps/web/src/components/auth/plate.tsx
6 apps/web/src/components/auth/shell.tsx
1 apps/web/src/components/canvas/account-menu.tsx
29 apps/web/src/components/canvas/actions.tsx
6 apps/web/src/components/canvas/add-dialog.tsx
4 apps/web/src/components/canvas/bottom-panel/log-stream.tsx
6 apps/web/src/components/canvas/bottom-panel/panel-frame.tsx
3 apps/web/src/components/canvas/bottom-panel/panel.tsx
6 apps/web/src/components/canvas/bottom-panel/reference-palette.tsx
1 apps/web/src/components/canvas/bottom-panel/step-icon.tsx
4 apps/web/src/components/canvas/bottom-panel/tabs/deployments.tsx
7 apps/web/src/components/canvas/bottom-panel/tabs/logs.tsx
4 apps/web/src/components/canvas/bottom-panel/tabs/settings.tsx
6 apps/web/src/components/canvas/bottom-panel/tabs/tracing.tsx
10 apps/web/src/components/canvas/bottom-panel/tabs/variables.tsx
12 apps/web/src/components/canvas/canvas.tsx
6 apps/web/src/components/canvas/copy-prompt.tsx
4 apps/web/src/components/canvas/endpoint-address.tsx
1 apps/web/src/components/canvas/environment.tsx
5 apps/web/src/components/canvas/errors.ts
7 apps/web/src/components/canvas/format.ts
4 apps/web/src/components/canvas/mapping.ts
1 apps/web/src/components/canvas/nodes/group-node.tsx
2 apps/web/src/components/canvas/nodes/icons.tsx
14 apps/web/src/components/canvas/nodes/node-shell.tsx
8 apps/web/src/components/canvas/nodes/node-toolbar.tsx
10 apps/web/src/components/canvas/nodes/runtime-line.tsx
5 apps/web/src/components/canvas/nodes/service-node.tsx
1 apps/web/src/components/canvas/nodes/volume-node.tsx
1 apps/web/src/components/canvas/not-wired.ts
16 apps/web/src/components/canvas/observability/axiom-gate.tsx
7 apps/web/src/components/canvas/observability/chrome.tsx
12 apps/web/src/components/canvas/observability/correlate.ts
29 apps/web/src/components/canvas/observability/explorer.tsx
17 apps/web/src/components/canvas/observability/lamp.tsx
10 apps/web/src/components/canvas/observability/log-context.tsx
6 apps/web/src/components/canvas/observability/page.tsx
12 apps/web/src/components/canvas/observability/stream.tsx
5 apps/web/src/components/canvas/primitives.tsx
7 apps/web/src/components/canvas/project-switcher.tsx
4 apps/web/src/components/canvas/rail.tsx
12 apps/web/src/components/canvas/settings.tsx
4 apps/web/src/components/canvas/store.tsx
2 apps/web/src/components/canvas/toolbar.tsx
4 apps/web/src/components/canvas/topbar.tsx
20 apps/web/src/components/canvas/types.ts
5 apps/web/src/components/canvas/use-data.ts
1 apps/web/src/components/canvas/use-debounced.ts
7 apps/web/src/components/canvas/use-deployment-link.ts
2 apps/web/src/components/canvas/use-hotkey.ts
1 apps/web/src/components/canvas/use-now.ts
6 apps/web/src/components/canvas/use-synced-graph.ts
6 apps/web/src/components/invite-dialog.tsx
1 apps/web/src/components/logo.tsx
17 apps/web/src/components/palette.tsx
1 apps/web/src/components/sign-in-form.tsx
3 apps/web/src/components/sign-up-form.tsx
11 apps/web/src/lib/ansi.tsx
4 apps/web/src/lib/ansi.worker.ts
39 apps/web/src/lib/api.ts
4 apps/web/src/lib/axiom-sign-in.ts
23 apps/web/src/lib/canvas-overlay.ts
7 apps/web/src/lib/panel-write.ts
52 apps/web/src/lib/query.ts
34 apps/web/src/lib/realtime.tsx
31 apps/web/src/lib/session.tsx
2 apps/web/src/main.tsx
1 apps/web/src/routes/__root.tsx
2 apps/web/src/routes/_auth/axiom/callback.tsx
10 apps/web/src/routes/_auth/device.tsx
10 apps/web/src/routes/_auth/p/$projectId.tsx
1 apps/web/src/routes/_auth/route.tsx
10 apps/web/src/routes/invite.$invitationId.tsx
7 apps/web/vite.config.ts
11 changes: 8 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ local.db
local.db-*
.data

# Local Convex state, Keel install state
**/.convex
infra
# Agent worktrees (full repo copies) and tool state
.claude
.agents

# keel serve state (KEEL_DATA_DIR in dev: make dev) and local binaries
.keel
**/keel.db*
bin
159 changes: 99 additions & 60 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: ci

# Pull requests run this directly. Pushes to main and v* tags run it as the first job of
# images.yml, which publishes images only when every job here passed.
# images.yml, which publishes the image only when every job here passed.
on:
pull_request:
workflow_call:
Expand All @@ -10,62 +10,85 @@ permissions:
contents: read

jobs:
check:
# The keel binary: one Go module at the repo root (serve, proxy, agent and the CLI). `./...`
# stops at nested modules.
go:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-go@v6
with:
bun-version-file: package.json
- run: bun install --frozen-lockfile
- run: bun run check-types
go-version-file: go.mod
- name: gofmt
run: |
# The root module's package directories only (gofmt alone would walk nested modules).
dirs=$(go list -f '{{.Dir}}' ./...)
# shellcheck disable=SC2086
unformatted=$(gofmt -l $dirs)
if [ -n "$unformatted" ]; then
echo "Not gofmt-ed:"; echo "$unformatted"
exit 1
fi
- run: go vet ./...
- name: No comments in Go
run: go run ./tools/nocomments $(go list -f '{{.Dir}}' ./...)
- run: go test ./...
- name: openapi.json is current
run: |
# The dashboard's hooks and the CLI are built against this file: a handler change must
# come with `make openapi`.
go run ./cmd/keel openapi > /tmp/openapi.json
if ! diff -u openapi.json /tmp/openapi.json; then
echo "openapi.json is stale: run make openapi and commit it"; exit 1
fi

# The keel CLI: its own Go module, outside bun and turbo.
cli:
# keel serve + keel agent in one test process against the runner's own fresh Swarm: deploy,
# ship a variable change, stop, start, redeploy, delete (internal/serve/swarm_it_test.go).
swarm:
runs-on: ubuntu-24.04
defaults:
run:
working-directory: apps/cli
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version-file: apps/cli/go.mod
cache-dependency-path: apps/cli/go.sum
- run: test -z "$(gofmt -l .)"
- run: go vet ./...
- run: go test ./...
go-version-file: go.mod
- name: Swarm
run: |
docker swarm init --advertise-addr 127.0.0.1
docker network create -d overlay --attachable --opt com.docker.network.driver.mtu=1200 keel
- run: go test ./internal/serve -run SwarmIT -v -count=1 -timeout 15m
env:
KEEL_IT_DOCKER_HOST: unix:///var/run/docker.sock

# keel-proxy's Caddy plugin (apps/proxy): its own Go module, built into the image by xcaddy.
proxy:
# The dashboard, embedded into the binary by the image build.
web:
runs-on: ubuntu-24.04
defaults:
run:
working-directory: apps/proxy
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
- uses: oven-sh/setup-bun@v2
with:
go-version-file: apps/proxy/go.mod
cache-dependency-path: apps/proxy/go.sum
- run: test -z "$(gofmt -l .)"
- run: go vet ./...
- run: go test ./...
bun-version-file: package.json
- run: bun install --frozen-lockfile
- name: No new comments
run: scripts/check-comments.sh
- run: bun run check-types
working-directory: packages/ui
- run: bun run check-types
working-directory: apps/web
- name: oxlint (type-aware, needs the generated client from check-types)
run: bunx oxlint --type-aware
- run: bun run build
working-directory: apps/web

# Runs install.sh end to end on a fresh runner against images built from this commit, twice:
# the second run must be a no-op upgrade that keeps every secret.
# Runs install.sh end to end on a fresh runner against the image built from this commit, twice:
# the second run must be a no-op upgrade that keeps every secret and the data.
install:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v3
- name: Build images
run: |
docker buildx build --load -t local/keel-web:ci -f apps/web/Dockerfile .
docker buildx build --load -t local/keel-functions:ci -f deploy/functions.Dockerfile .
docker buildx build --load -t local/keel-worker:ci apps/worker
docker buildx build --load -t local/keel-proxy:ci apps/proxy
- name: Configure the installer for local images
- name: Build the image
run: docker buildx build --load --build-arg VERSION=ci -t local/keel:ci .
- name: Configure the installer for the local image
run: |
# Swarm will not advertise on loopback; use the runner's primary address.
{
Expand All @@ -77,48 +100,53 @@ jobs:
echo "KEEL_JSON=1"
} >> "$GITHUB_ENV"
- run: sudo -E ./install.sh > first.json
- run: jq -e '.ok == true and .url == "http://" + env.KEEL_ADDR' first.json
- name: The installer's JSON result
run: |
jq -e '.ok == true and .url == "http://" + env.KEEL_ADDR and .apiUrl == .url' first.json
jq -e '.version == "ci" and .stateDir == "/opt/keel" and (.publicIp | type) == "string" and (.warnings | type) == "array"' first.json
- name: Sign up and sign in against the installed control plane
run: |
site="http://$KEEL_ADDR:3211"
site="http://$KEEL_ADDR"
creds='"email":"ci@example.com","password":"correct-horse-battery"'
post() { curl -fsS -X POST -H 'Content-Type: application/json' -H "Origin: http://$KEEL_ADDR" "$site/api/auth/$1" -d "$2"; }
post sign-up/email "{$creds,\"name\":\"CI\"}" | jq -e .user.id
post sign-in/email "{$creds}" | jq -e .token
post() { curl -fsS -X POST -H 'Content-Type: application/json' -H "Origin: $site" "$@"; }
curl -fsS "$site/api/auth/sign-up-open" | jq -e '.open == true'
# Sign-up sets the dashboard's cookie and returns the same session as a bearer token.
post -c cookies.txt "$site/api/auth/sign-up" -d "{$creds,\"name\":\"CI\"}" | jq -e '.token | length > 0'
curl -fsS -b cookies.txt "$site/api/me" | jq -e '.user.email == "ci@example.com"'
token=$(post "$site/api/auth/sign-in" -d "{$creds}" | jq -r .token)
curl -fsS -H "Authorization: Bearer $token" "$site/api/me" | jq -e '.user.email == "ci@example.com"'
curl -fsS "$site/api/me" | jq -e '.user == null'
# Once an account exists, sign-up is by invitation only.
! post sign-up/email '{"email":"second@example.com","password":"correct-horse-battery","name":"Second"}'
curl -fsS "$site/api/auth/sign-up-open" | jq -e '.open == false'
! post "$site/api/auth/sign-up" -d '{"email":"second@example.com","password":"correct-horse-battery","name":"Second"}'
- uses: actions/setup-go@v6
with:
go-version-file: apps/cli/go.mod
cache-dependency-path: apps/cli/go.sum
go-version-file: go.mod
- name: keel CLI finds the install from the dashboard URL and logs in through an approved link
working-directory: apps/cli
run: |
go build -o "$RUNNER_TEMP/keel" ./cmd/keel
export KEEL_CONFIG_DIR="$RUNNER_TEMP/keel-config"
keel() { "$RUNNER_TEMP/keel" "$@"; }
site="http://$KEEL_ADDR:3211"
auth() { curl -fsS -H 'Content-Type: application/json' -H "Origin: http://$KEEL_ADDR" "$@"; }
site="http://$KEEL_ADDR"
auth() { curl -fsS -H 'Content-Type: application/json' -H "Origin: $site" "$@"; }
# No terminal: login returns at once with the link, and commands wait for the approval.
keel login "http://$KEEL_ADDR" > pending.json
keel login "$site" > pending.json
jq -e '.ok and .status == "pending" and (.approvalUrl | startswith("http://" + env.KEEL_ADDR + "/device?user_code="))' pending.json
code=$(jq -r .code pending.json)
! keel whoami > early.json
jq -e '.code == "AUTHORIZATION_PENDING"' early.json
# The human: signed in, opens the link (binds the code to them) and approves.
token=$(auth -X POST "$site/api/auth/sign-in/email" -d '{"email":"ci@example.com","password":"correct-horse-battery"}' | jq -r .token)
token=$(auth -X POST "$site/api/auth/sign-in" -d '{"email":"ci@example.com","password":"correct-horse-battery"}' | jq -r .token)
auth -H "Authorization: Bearer $token" "$site/api/auth/device?user_code=$code" | jq -e '.status == "pending"'
auth -X POST -H "Authorization: Bearer $token" "$site/api/auth/device/approve" -d "{\"userCode\":\"$code\"}" | jq -e .success
auth -X POST -H "Authorization: Bearer $token" "$site/api/auth/device/approve" -d "{\"userCode\":\"$code\"}"
# The next command finishes the login.
keel whoami | jq -e '.ok and .user.email == "ci@example.com" and .url == "http://" + env.KEEL_ADDR'
keel login | jq -e '.ok and .status == "loggedIn"'
- name: keel CLI creates a project and stages a service in it, as an agent would
working-directory: apps/cli
run: |
export KEEL_CONFIG_DIR="$RUNNER_TEMP/keel-config"
keel() { "$RUNNER_TEMP/keel" "$@"; }
# This account never opened the dashboard, so there is no organization yet: no projects,
# and the first project create founds it.
# Sign-up founded this account's organization; it has no project until the first create.
keel project list | jq -e '.ok and .projects == []'
keel project create "CI App" --link | jq -e '.ok and .project.slug == "ci-app" and .project.environments[0].name == "production" and .url == "http://" + env.KEEL_ADDR + "/p/ci-app"'
! keel project create ci-app > taken.json
Expand All @@ -134,24 +162,35 @@ jobs:
jq -e '.code == "USAGE"' unconfirmed.json
keel service delete api --yes | jq -e '.ok and .deleted'
keel service list | jq -e '.services == []'
- name: keel-proxy answers Convex on the shared admin socket and listens on nothing yet
- name: keel proxy answers serve on the shared admin socket, listens on nothing yet, and has no Docker socket
run: |
admin() { sudo docker compose -p keel exec -T backend curl -fsS --unix-socket /run/keel-proxy/admin.sock "http://proxy$1"; }
# The admin socket lives in the proxy-admin volume; read it from the host.
sock="$(sudo docker volume inspect -f '{{.Mountpoint}}' keel_proxy-admin)/admin.sock"
admin() { sudo curl -fsS --unix-socket "$sock" "http://proxy$1"; }
admin /keel/host-addrs | jq -e 'type == "array" and length > 0'
# migrations:run synced it: nothing is exposed, so no app and no public listener.
# serve synced it at start: nothing is exposed, so no app and no public listener.
admin /config/ | jq -e '(.apps // {}) == {}'
- name: Re-run is idempotent
# The container that parses internet traffic never holds the Docker socket.
proxy=$(sudo docker compose -p keel --env-file /opt/keel/.env -f /opt/keel/compose.yml ps -q proxy)
mounts=$(sudo docker inspect -f '{{range .Mounts}}{{.Source}} {{end}}' "$proxy")
echo "proxy mounts: $mounts"
[[ -n "$mounts" && "$mounts" != *docker.sock* ]]
- name: Re-run is idempotent and keeps the data
run: |
secrets() { sudo grep -E '^(INSTANCE_SECRET|BETTER_AUTH_SECRET|KEEL_WORKER_TOKEN|CONVEX_SELF_HOSTED_ADMIN_KEY)=' /opt/keel/.env | sha256sum; }
secrets() { sudo grep -E '^KEEL_WORKER_TOKEN=' /opt/keel/.env | sha256sum; }
before=$(secrets)
sudo -E ./install.sh > second.json
jq -e '.ok == true' second.json
[ "$before" = "$(secrets)" ]
# The account, the CLI's session and the project survive it.
export KEEL_CONFIG_DIR="$RUNNER_TEMP/keel-config"
"$RUNNER_TEMP/keel" whoami | jq -e '.ok and .user.email == "ci@example.com"'
"$RUNNER_TEMP/keel" project list | jq -e '[.projects[].slug] == ["ci-app"]'
- name: Diagnostics
if: failure()
run: |
cat first.json second.json 2>/dev/null || true
sudo docker compose -p keel ps || true
sudo docker compose -p keel logs --tail 200 || true
sudo docker service ps keel-worker --no-trunc || true
sudo docker service logs keel-worker --tail 100 || true
sudo docker service ps keel-agent --no-trunc || true
sudo docker service logs keel-agent --tail 100 || true
Loading
Loading