Central machine log streaming + tail (fold logs) - #2
Merged
Conversation
App stdout/stderr was only available as a one-shot per-machine snapshot
pulled from the host admin port — not centralized, not tailable, lost on
restart, and requiring host reachability. This wires up a full path so
`fold logs` shows live app output.
- Runtime gains LogsSince for incremental capture: docker uses
`docker logs --timestamps` with a full-precision per-machine cursor so
shipping is de-duplicated across ticks; wasm tracks a buffer read offset;
firecracker/microvm are no-ops (serial console still goes to the host
journal — per-machine capture there stays roadmap).
- Agents ship new lines each heartbeat to POST /v1/agent/logs (separate
from the heartbeat payload to keep the reconcile path lean).
- Control resolves each line's machine to its app and persists to a new
machine_logs table, bounded to the most recent lines per app.
- GET /v1/apps/{app}/applogs streams them over SSE (recent backlog, then
follow), filterable by ?machine=.
- `fold logs` now tails app output by default (--machine to filter,
--no-follow for a snapshot); `fold logs --system` keeps the activity
stream (deploy/scale/build). `fold deploy`'s build streaming is
unchanged.
Tests: store roundtrip + retention, docker log parser + dedup cursor,
wasm buffer cursor, control machine->app mapping (drops unknown machines),
and an API end-to-end (agent-only ingest auth + owner tail + cross-org
denial). Verified with make e2e-local and a manual deploy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Next roadmap item after the security/reliability hardening: operability. App stdout/stderr was only available as a one-shot per-machine snapshot pulled from the host admin port (
FetchMachineLogs) — not centralized, not tailable, capped at a tail, lost when the machine/host dies, and requiring direct host reachability. For an AI-agent runtime, not being able to see what your deployed agent prints is the biggest user-facing gap. This wires up a full central log path sofold logstails live app output.What changed
Capture (agent/runtime).
RuntimegainsLogsSincefor incremental capture:docker logs --timestampswith a full-precision per-machine cursor, so lines ship exactly once across ticks (the cursor isn't lost to the millisecond rounding of the wire type).Ship (agent → control). Agents push new lines each heartbeat to a new agent-authed
POST /v1/agent/logs, separate from the heartbeat payload so the reconcile path stays lean. Per-tick payload is capped.Persist (control/store). Control resolves each line's machine to its owning app (dropping lines for unknown machines) and writes to a new
machine_logstable, bounded to the most recent lines per app (oldest trimmed on insert).Tail (api/cli).
GET /v1/apps/{app}/applogsstreams over SSE (recent backlog, then follow), filterable by?machine=. The CLI:fold logs— tails app stdout/stderr across machines (default follow)fold logs --machine <id>— filter to one machinefold logs --no-follow— print backlog and exitfold logs --system— the activity stream (deploy/scale/build), unchangedfold deploy's live build-output streaming is untouched (still the activity SSE).CLI UX note
fold logsnow defaults to app output (conventional, like fly/heroku/kubectl) rather than the activity event stream. Activity moves under--system. Chosen deliberately over keeping the old default.Tests
--timestampsparser + dedup cursor, wasm bufferReadNewVerification
gofmt -lclean ·go vet ./...clean ·go test ./...all pass ·go build -tags firecracker ./...compilesmake e2e-localpasses (no regression in deploy/scale-to-zero/wake)hello-golocally and confirmedfold logsshows its stdout exactly once (dedup verified),--systemshows build/deploy events🤖 Generated with Claude Code