candaws

command
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

CandaWS

Five parody cloud services, five CSP engines, five generated widgets, one binary. "Monolithic microservices" taken literally.

go run ./examples/widget/candaws                 # http://127.0.0.1:8081
go run ./examples/widget/candaws -pace 0.25      # everything four times faster
go run ./examples/widget/candaws -trouble 0      # nothing fails anywhere
go run ./examples/widget/candaws -seed 7         # a different fleet, reproducibly
go run ./examples/widget/candaws -goroutines 5s  # the scheduler's own census

-pace is accepted in [0.001, 1000] and refused by name outside it, rather than passed on to become an engine complaining about an interval nobody chose. -trouble reads a negative value as zero, and the banner prints the pace and trouble the fleet is actually running at.

Service Category parodied Engine shape
Yakshave continuous-delivery pipeline a chain of stage goroutines, closed from the head
Queuecumber managed message queue a broker answering lease requests on their own reply channels
Blobfish object store a coordinator that stops counting at the write quorum
Coldstart function-as-a-service a dispatcher that spawns and reaps instance goroutines
Dashbored metrics and dashboards a fan-in with a counted shutdown

docs/fleet.md is the design — the roster, the build order and the ontology table behind each service. A sixth, Roundabout, is deliberately not built: it is the reserved task for the agentic A/B benchmark, and docs/bench.md is grader-side.

What was written by hand, and what was not

Every card is generated, from the five .widget documents in docs/ through bash pkg/widget/gen.sh. The state, the reducers, the bindings, the labels, the SVG scenes, the motion gates, the legends and every word on every card come out of those documents; each service directory's view.templ, view_templ.go and widget.gen.go open with a generated marker and are overwritten on the next run.

What is hand-written is each service's engine and the ten-line seam between it and its card. seam.go in each package is the only file that knows both halves exist: it maps one view onto the wire field names the document declared, using the generated constants, so renaming a field in a document is a compile error rather than a card that silently stops updating. No engine file names a region, a wire name or a field spelling; no generated file knows there are goroutines behind it.

main.go and fleet.go are this host: they build the five engines, register the five widgets, resolve the six sources their declared streams name — six and not five, because Yakshave declares two — and serve the result. ../hosting is the plumbing both this host and the SDK's smaller one share: the Origin allowlist, the palette resolution, the region rendering and the router. Neither host builds an http.ServeMux; there is exactly one correct arrangement of those two routes, and two copies of it was two chances to get the mount path or the ordering wrong.

What a ps-eye view shows

One process. One port. No container, no sidecar, no service mesh, and no inter-service call that is not a channel send.

Inside it, measured with -goroutines:

candaws: 47 goroutines at steady state, 6s in

That is the whole fleet with no browser connected: five engines, the HTTP listener, the signal watcher and the Go runtime's own. It breaks down as the sum of what each engine documents, plus one goroutine per engine holding its Run:

Service Goroutines What they are
Yakshave 11 two feeds, an observer, a meter, the head, a collector, four stages, Run
Queuecumber 8 a feed, the broker, the expiry sweep, the producer, three workers, Run
Blobfish 9 a feed, an observer, the coordinator, the repairer, the bucket, three zones, Run
Coldstart 4 + n a feed, the dispatcher, the caller, Run — plus one per live instance and one per in-flight call, which come and go by design
Dashbored 8 a feed, an observer, the aggregator, the alerter, three collectors, Run

A browser session adds one session goroutine plus one per effect, and this host opens six. The end-to-end specification measures it rather than asserting it from a table: against a bare test binary's six goroutines, the fleet plus one live session runs at 58 — the same 40-odd, plus the session, plus its six sources, plus whatever Coldstart happens to have warm.

Registering a sixth service would add its engine's goroutines, one fragment, some event names and a slice index. It would not add a port, a process or a deployment.

What is actually checked

  • Engine specifications, -race green, one suite per service. Every one of the five has a file that starts no goroutine at all, because the interesting half of each engine is a pure function: Yakshave's stage registry and its view fold, Queuecumber's conservation law, Blobfish's replica rule table, Coldstart's temperature ladder, Dashbored's reservoir, histogram and merge. The running specifications are then about the concurrency and nothing else.
  • Card assertions on rendered output only. Every service has a card_test.go that mounts its widget through pkg/widget/widgettest and asserts substrings, counts, declaration order and the landmark — never source. A generated card and a hand-written one share no file names and no formatting, and what comes out of Render is the only thing both can be held to. The BEN probe's own fragment fixture needs exactly that shape.
  • live_test.go opens a real WebSocket against the real handler — a real handshake, real protobuf frames, all five engines running — and asserts that the pipeline card carries a stage the chain is actually in, that the console card carries an ingest rate the collectors actually produced, that a browser's own toggle comes back on the same connection, and that the prewarm command, which changes no widget state at all, reaches an engine and warms something.
  • gen.sh --check asserts every committed generated file is byte-identical to a fresh generation, so a document edited without regenerating fails.

The one thing that is not here

Roundabout. Building any part of it voids the benchmark probe.

Documentation

Overview

Command candaws is the CandaWS fleet: five parody cloud services, five engines, five generated widgets, and one binary.

Monolithic microservices, taken literally

A continuous-delivery pipeline, a message queue, an object store, a function-as-a-service runtime and a metrics service. In a real cloud those are five products, five control planes and five bills. Here they are five packages in one process, and what separates any two of them is what separates two goroutines: nothing but the scheduler. There is no per-service port, no per-widget connection, and no inter-service call that is not a channel send.

Running it

go run ./examples/widget/candaws                  # http://127.0.0.1:8081
go run ./examples/widget/candaws -addr :9000
go run ./examples/widget/candaws -seed 7          # a different fleet, reproducibly
go run ./examples/widget/candaws -pace 0.25       # everything four times faster
go run ./examples/widget/candaws -trouble 0       # nothing fails anywhere
go run ./examples/widget/candaws -goroutines 5s   # report the scheduler's own census

-pace multiplies every interval in the fleet and -trouble sets every probability, so the two flags between them cover the pacing knobs each engine exposes without asking anybody to remember five names for one idea. The per-service knobs are still there, in each package's Config.

Both flags answer for themselves at their edges. -pace is accepted in [0.001, 1000] and refused by name outside it, rather than passed on to become an engine reporting an interval it never chose. -trouble reads a negative value as zero — the fleet where nothing goes wrong — and the banner prints the value the fleet is actually running at.

What is generated and what is not

Nothing on the page was hand-written. The five widget documents in docs/ are turned into examples/widget/candaws/<service>/{view.templ,widget.gen.go} by pkg/widget/gen.sh — the state, the reducers, the bindings, the labels, the SVG scenes, the motion and every word on every card. This file registers them, resolves the six sources their declared streams name against the five engines, hands the registry the four security decisions a library may not make for a host, and serves the result.

Yakshave declares two streams, which is why there are six sources and not five: one for its runs and one for its minutes, because the two tickers behind them are two.

Two commands the host acts on

Queuecumber's redrive button and Coldstart's prewarm button emit events that change no widget state at all. The registry routes them to their widgets, whose reducers do nothing with them; this host wraps the registry's reducer and schedules an effect of its own, which is where a redrive and a prewarm actually happen. That is the same seam a stream's source name is: the document declares that the event exists, and the host decides what it means.

Security posture

live.Anonymous, live.AllowAll[live.AnonymousIdentity] and live.NoCSRFCheck, each because a single-page demo has no accounts to check against. Origins is a real allowlist derived from the listen address rather than live.AnyOrigin.

templ: version: v0.3.1020

Directories

Path Synopsis
Package blobfish is the generated Blobfish widget.
Package blobfish is the generated Blobfish widget.
Package coldstart is the generated Coldstart widget.
Package coldstart is the generated Coldstart widget.
Package dashbored is the generated Dashbored widget.
Package dashbored is the generated Dashbored widget.
Package fleet is the part of every CandaWS engine that is the same part.
Package fleet is the part of every CandaWS engine that is the same part.
Package queuecumber is the generated Queuecumber widget.
Package queuecumber is the generated Queuecumber widget.
Package yakshave is the generated Yakshave widget.
Package yakshave is the generated Yakshave widget.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL