Repository navigation
v1.12.9
Forge Framework v1.12.9 (2026-10-16T01:43:52Z)
Welcome to this new release of Forge Framework!
Changelog
New Features
- 64bbf0a: feat(cli): add forge dev --observability to send an app's telemetry to the local stack (@juicycleff)
- c5ad464: feat(cli): add forge observability up, down, status and export (@juicycleff)
- a822add: feat(cli): export the Forge Service dashboard and rules for your own Grafana (@juicycleff)
- ffab61d: feat(cli): generate the Grafana stack's provisioning and scrape targets (@juicycleff)
- a6c2f43: feat(cli): render the Forge Service dashboard and Prometheus alert rules (@juicycleff)
- ddfe4f1: feat(cli): run the grafana/otel-lgtm stack as one long-lived container (@juicycleff)
- 8d2247e: feat(conduit): add a leased service registry to Redis Streams (@juicycleff)
- 103030f: feat(conduit): add a leased service registry to the PostgreSQL provider (@juicycleff)
- 41ac764: feat(conduit): add consumer controls and backfill to Redis Streams (@juicycleff)
- 6d76b8d: feat(conduit): add consumer controls and backfill to the PostgreSQL provider (@juicycleff)
- 26852dc: feat(conduit): add dead letters and retention to the PostgreSQL provider (@juicycleff)
- 4c2bb0e: feat(conduit): add request and reply RPC to Redis Streams (@juicycleff)
- 54717e4: feat(conduit): add request and reply RPC to the PostgreSQL provider (@juicycleff)
- 5c54cba: feat(conduit): add the PostgreSQL provider's schema and streams (@juicycleff)
- c70a094: feat(conduit): carry trace context in envelope headers from prepare to handler (@juicycleff)
- 49b999b: feat(conduit): deliver PostgreSQL messages in commit-safe order (@juicycleff)
- d612aae: feat(conduit): log PostgreSQL listener outages (@juicycleff)
- f7b2dcb: feat(conduit): publish to PostgreSQL with transactional wakeups (@juicycleff)
- 4222652: feat(conduit): read PostgreSQL messages after a position (@juicycleff)
- 05e4a48: feat(conduit): repair a restored PostgreSQL database with Rebase (@juicycleff)
- 39ee763: feat(dashboard): add a span processor that feeds the trace store from OTel spans (@juicycleff)
- d054454: feat(dashboard): feed the trace viewer from the app's OTel spans (@juicycleff)
- 70b31c7: feat(deploy): accept an observability block on kubernetes targets (@juicycleff)
- 8aef1f9: feat(deploy): render ServiceMonitors and OTEL_* env for kubernetes observability (@juicycleff)
- f326f24: feat(events): trace events from publish to handler and switch to semconv durations (@juicycleff)
- 8836a2a: feat(extensions): read the config file in features and auth (@juicycleff)
- 61034d4: feat(extensions): use snake_case config keys in mcp, cache and security (@juicycleff)
- 04dd2c1: feat(grpc)!: record rpc.server.call.duration in place of the grpc_* series (@juicycleff)
- c96d1d9: feat(grpc)!: start the server at the end of app.Start so other extensions can register (@juicycleff)
- acf3cd3: feat(grpc): register app services with WithService and WithServiceRegistrar (@juicycleff)
- d6d69b8: feat(grpc): report checker changes from health Watch and statuses from List (@juicycleff)
- 6a83e26: feat(grpc): trace outbound gRPC calls from conduit and ClientDialOptions (@juicycleff)
- 78e33f3: feat(grpc): trace served calls with otelgrpc and put trace IDs on the log context (@juicycleff)
- e275ff3: feat(kafka): trace records from send to handler and record semconv durations (@juicycleff)
- 3f0b55a: feat(metrics)!: semconv HTTP server metrics replace the unlabelled forge_http series (@juicycleff)
- d463379: feat(metrics)!: stream connection metrics, with stream routes timed by their handshake (@juicycleff)
- c143e1f: feat(metrics): add an OTLP producer over the registry stream (@juicycleff)
- 54cf85c: feat(metrics): cap series per metric and register WithLabels children (@juicycleff)
- 103a23d: feat(metrics): export semconv names unprefixed with unit suffixes and exemplars (@juicycleff)
- fb16bb2: feat(metrics): label vectors for custom metrics (@juicycleff)
- 0f36f31: feat(metrics): let the collector hand out an OTLP producer (@juicycleff)
- 78fdb85: feat(metrics): push Go and process collector families with their scrape names (@juicycleff)
- 2b0990e: feat(metrics): push metrics over OTLP when an endpoint is configured (@juicycleff)
- b589d88: feat(metrics): report delta temporality from the OTLP producer (@juicycleff)
- 0276c4b: feat(metrics): semconv HTTP server metrics keyed by matched route (@juicycleff)
- bf818fb: feat(profiling): push profiles to Pyroscope through grafana/pyroscope-go (@juicycleff)
- 646cf80: feat(profiling): resolve profiling config from code, forge config and PYROSCOPE_* env (@juicycleff)
- 35e5019: feat(queue): trace messages from publish to handler and record messaging durations (@juicycleff)
- 7c4648b: feat(router): mark stream routes established after upgrade or first flush (@juicycleff)
- ab7a043: feat(router): record 5xx errors and recovered panics on the request span (@juicycleff)
- 05ea988: feat(router): report a stream route's kind and establishment to outer middleware (@juicycleff)
- afa7096: feat(router): report stream open, close and messages to a stream observer (@juicycleff)
- 325bb6d: feat(router): report the matched route pattern to outer middleware (@juicycleff)
- 724272e: feat(shared): report the matched route through RouteMatch.OnMatched (@juicycleff)
- 258dc92: feat(telemetry): OTLP/HTTP tracer provider with rate-limited export errors (@juicycleff)
- 68dc4e2: feat(telemetry): instrumented outbound HTTP client and transport (@juicycleff)
- 8246fed: feat(telemetry): label sampled requests with span_id and span_name for the CPU profiler (@juicycleff)
- ba6f9c6: feat(telemetry): push metrics over OTLP/HTTP from a private meter provider (@juicycleff)
- 3efb70a: feat(telemetry): resolve OTLP metrics export settings from code, config and env (@juicycleff)
- 781a643: feat(telemetry): resolve tracing config from code, file and OTEL env (@juicycleff)
- f168762: feat(telemetry): server span middleware named after the matched route (@juicycleff)
- 7311718: feat(telemetry): shared resource and parent-based sampler (@juicycleff)
- e3a2f7f: feat(tracing): add the messaging package and trace context helpers for message headers (@juicycleff)
- a9f80f8: feat(tracing): always sample lifecycle roots and let processors ask for unsampled spans (@juicycleff)
- 1f45347: feat(tracing): trace extension register, start and stop (@juicycleff)
- f9f3148: feat: histogram units in HistogramOpts and span ID log helpers in forge (@juicycleff)
- 2c9d05c: feat: push profiles to Pyroscope and link sampled spans to their CPU profile (@juicycleff)
- 117182f: feat: trace every request and export over OTLP when an endpoint is set (@juicycleff)
Bug Fixes
- f9ddbb4: fix(cli): catch ports Docker Desktop holds and clean up after a failed stack start (@juicycleff)
- c8f5fcc: fix(cli): find grove's migrations when central migrations are on (@juicycleff)
- 71de4a6: fix(cli): keep metrics on the scrape path when forge sends traces to the stack (@juicycleff)
- 0473f1c: fix(cli): let forge dev --docker build projects with local replaces (@juicycleff)
- b0f1622: fix(cli): pin streaming client generator fixes (@juicycleff)
- a070a2e: fix(cli): pin the forge release with nullable array item types (@juicycleff)
- 97938c1: fix(cli): point the busy-port hint at forge obs up (@juicycleff)
- 62ab4f4: fix(cli): wire forge dev --docker -O to the stack over a shared docker network (@juicycleff)
- a52b18c: fix(client): preserve streaming protocols and omit unused helpers (@juicycleff)
- ffe531f: fix(conduit): accept pool settings in the PostgreSQL listener URL (@juicycleff)
- 020f0ae: fix(conduit): back off PostgreSQL re-polls while a transaction holds the horizon (@juicycleff)
- 4ab0246: fix(conduit): back off a PostgreSQL listener whose connection keeps dropping (@juicycleff)
- 70cb250: fix(conduit): bound the PostgreSQL backlog window in the index scan (@juicycleff)
- 72afed7: fix(conduit): close PostgreSQL replay, stale reply and empty subject gaps (@juicycleff)
- a411066: fix(conduit): fail loudly when a PostgreSQL cursor is ahead of the cluster (@juicycleff)
- 16485af: fix(conduit): give a retried PostgreSQL RPC its own reply (@juicycleff)
- 66854d0: fix(conduit): keep automatic discovery and RPC choices working on upgrade (@juicycleff)
- c8cfb1b: fix(conduit): keep the app's notification handler off the PostgreSQL listener (@juicycleff)
- 7afd612: fix(conduit): leave the memory provider out of automatic RPC selection (@juicycleff)
- 6aefa96: fix(conduit): let the PostgreSQL listener connect to a direct URL (@juicycleff)
- e699e92: fix(conduit): migrate PostgreSQL without the database CREATE privilege (@juicycleff)
- d68aa70: fix(conduit): order PostgreSQL delivery by transaction ID, not its text (@juicycleff)
- 80d916b: fix(conduit): prune only below the visibility horizon (@juicycleff)
- 2ca61cc: fix(conduit): reject a conflicting PostgreSQL backfill before claiming it (@juicycleff)
- f3917e5: fix(conduit): repair start snapshots and reroute Redis requests from closing servers (@juicycleff)
- 59cb9cd: fix(conduit): report a Redis read that hits the caller's deadline as a deadline (@juicycleff)
- 0ce2989: fix(conduit): report the whole PostgreSQL listener outage and skip its PG13-only test (@juicycleff)
- d4987f1: fix(conduit): route Redis RPC replies to the call that sent them (@juicycleff)
- 2f80c1e: fix(conduit): serve every Redis RPC procedure from one reader (@juicycleff)
- 07df295: fix(conduit): skip PostgreSQL DDL when the schema is current (@juicycleff)
- cc31742: fix(conduit): stop Redis RPC from starting a reply reader after Close (@juicycleff)
- 9cc08fb: fix(conduit): stop idle PostgreSQL subscribers from busy polling and pinning the horizon (@juicycleff)
- 77de49c: fix(conduit): sweep every aged-out PostgreSQL message on each pass (@juicycleff)
- 0b2f776: fix(conduit): track pruned positions so a read after one is never wrongly resumable (@juicycleff)
- 6ad41dd: fix(conduit): wake only the PostgreSQL caller a reply belongs to (@juicycleff)
- 40d916a: fix(conduit): walk large PostgreSQL backlogs in bounded windows (@juicycleff)
- 79f6e9f: fix(config): ignore config values that don't convert and log a warning (@juicycleff)
- 69857c6: fix(config): let an option set a field to its zero value (@juicycleff)
- eb8f1c6: fix(config): let config files override extension defaults (@juicycleff)
- eb2d938: fix(dashboard): read the stream broker atomically and register the span processor last (@juicycleff)
- c2d6c33: fix(dashboard): stop GetTrace writing shared spans and add an exchange side channel (@juicycleff)
- c8dff6f: fix(deploy): find the ServiceMonitor API through discovery instead of reading CRDs (@juicycleff)
- fa11e8f: fix(grpc)!: fail Start when TLS is on and the credentials don't load (@juicycleff)
- 92adeee: fix(grpc): drop the server when Start fails to listen (@juicycleff)
- 977f62f: fix(grpc): keep services registered before Start (@juicycleff)
- d03e129: fix(grpc): put trace_id and span_id on the server's own log lines (@juicycleff)
- f867897: fix(grpc): register the example's services before the server starts (@juicycleff)
- ca6120b: fix(grpc): stop GracefulStop deadlocking on calls in flight (@juicycleff)
- 70c8fbf: fix(health): stop doubling the forge_ prefix and give durations seconds buckets (@juicycleff)
- b4c986b: fix(kafka): inject the async send span's context into the record (@juicycleff)
- 502586c: fix(kafka): send the headers and timestamp SendMessages was dropping, add ctx-aware calls (@juicycleff)
- 0b29c58: fix(kafka): skip telemetry for an empty batch, bound conduit tracing test waits (@juicycleff)
- 2979f1b: fix(metrics): ask go-utils for timer quantiles as fractions (@juicycleff)
- 0eedc90: fix(metrics): count Winsock resets as a client leaving on Windows (@juicycleff)
- e4b0835: fix(metrics): count a write to a vanished stream client as a clean close (@juicycleff)
- 4e3a215: fix(metrics): honour limits.max_metrics and warn once when it is reached (@juicycleff)
- 416df98: fix(metrics): keep a cut-short OTLP produce from letting a colliding series flip owner (@juicycleff)
- e7cf20d: fix(metrics): keep series state when a producer walk is cut short (@juicycleff)
- 2b42bc0: fix(metrics): let the config file change metrics defaults when code sets none (@juicycleff)
- 4ebe8ce: fix(metrics): record only the handshake of a stream route as an HTTP request (@juicycleff)
- 34a74a8: fix(metrics): record stream connections when HTTP metrics are off (@juicycleff)
- eec946f: fix(metrics): settle OTLP series collisions and bucket edge cases before touching state (@juicycleff)
- a7cfa59: fix(metrics): start the collector from app.Start and export host series with labels (@juicycleff)
- 7cc7cca: fix(middleware): keep context values in the handler behind Compress (@juicycleff)
- 00e97a4: fix(profiling): cap the final upload at 5 seconds and clean tag values (@juicycleff)
- 6d8c62e: fix(router): cap WebTransport uni streams with MaxUniStreams (@juicycleff)
- ce4850f: fix(router): close streams before reporting them closed and guard nil observer handles (@juicycleff)
- bf128d7: fix(router): deprecate WebTransportConfig.EnableDatagrams (@juicycleff)
- 9122add: fix(router): don't let a stalled socket hold up the shutdown drain (@juicycleff)
- 8517d90: fix(router): end open streams when a graceful shutdown starts (@juicycleff)
- a49f64b: fix(router): enforce WebTransportConfig.MaxDatagramFrameSize (@juicycleff)
- 759bd5c: fix(router): keep WebTransport stream JSON values apart on read and write (@juicycleff)
- d59f6f9: fix(router): let handlers read what middleware stores with ctx.Set (@juicycleff)
- 1d52782: fix(router): make WebTransport upgrades work on forge's HTTP/3 server (@juicycleff)
- efb2a6c: fix(router): refuse a nil middleware when you register it (@juicycleff)
- c96a107: fix(router): serve "/" when you mount a handler at the root (@juicycleff)
- 0929de5: fix(scheduler): keep a panicking panic handler from taking the process down (@juicycleff)
- c316528: fix(scheduler): report recovered job panics with the job name and stack (@juicycleff)
- 6fb5a7a: fix(system): validate filesystem counters before calculating disk usage (@juicycleff)
- 36effde: fix(telemetry): drop stream kind from rejected upgrades and skip excluded paths (@juicycleff)
- c75cd99: fix(telemetry): end a stream route's span at the handshake (@juicycleff)
- 10e1b87: fix(telemetry): keep nested dispatch off the outer span and propagate when disabled (@juicycleff)
- f6a8d67: fix(telemetry): push metrics only while the app runs, from Start to Stop (@juicycleff)
- 5e2623e: fix(telemetry): reject NaN sample ratios and keep literal plus in header values (@juicycleff)
- bbb8ff6: fix(telemetry): reject OTLP endpoints that are not http or https URLs (@juicycleff)
- 296c8f6: fix(telemetry): trim OTLP endpoint env, copy headers and only warn when metrics push is on (@juicycleff)
- 94169ce: fix(telemetry): warn when tracing config can't be read (@juicycleff)
- 9bf4159: fix(tracing): let errors.Is reach a panicked error value (@juicycleff)
- 23956ac: fix: bumped confy version (@juicycleff)
- 73b7d2f: fix: fixed confy config bug (@juicycleff)
- 73ac884: fix: fixed forge cli version (@juicycleff)
Performance Improvements
- 5df4f44: perf(dashboard): read the trace ingest gate without taking the store lock (@juicycleff)
- f4bef5f: perf(metrics): cut the HTTP server metrics middleware to one allocation of state (@juicycleff)
- c39239f: perf(metrics): let ObserveWithExemplar stamp the exemplar time (@juicycleff)
- b53757e: perf(metrics): look up vec children without allocating the key (@juicycleff)
- 69f2671: perf(metrics): skip the request copy when the route holder is already set (@juicycleff)
- 75d06cf: perf(router)!: run tracing and HTTP metrics outside the forge global chain (@juicycleff)
- 12efdf5: perf(shared): give OnEstablished a third inline slot (@juicycleff)
- 30ff05f: perf(shared): keep the first OnMatched callback inline (@juicycleff)
- 7d9b116: perf(telemetry)!: set a server span's request attributes when it ends (@juicycleff)
- 3c1ac98: perf(telemetry): cut the tracing middleware's own allocations (@juicycleff)
- 8b690f0: perf(telemetry): trim the tracing middleware's per-request work (@juicycleff)
Refactoring
- 364e9db: refactor(consensus)!: remove the unused TracingManager (@juicycleff)
- 00d4b26: refactor(events): delete the bus worker pool that nothing ever fed (@juicycleff)
- c13b722: refactor(logger): share the rate-limited warn logger tracing uses for export errors (@juicycleff)
- 786e126: refactor(metrics)!: delete the StatsD and Influx formatters (@juicycleff)
- 9591a16: refactor(metrics): read custom collector values in one place (@juicycleff)
- 5fa4b1b: refactor(telemetry): remove the unused observability package and Jaeger exporter (@juicycleff)
Documentation Updates
- 2149cbb: docs(changelog): update CHANGELOG.md for v1.12.8 (@github-actions[bot])
- 479f075: docs(cli): document forge observability and the deploy observability block (@juicycleff)
- 1c53cdb: docs(cli): note the stack's docker network in llm.txt (@juicycleff)
- b6bdf48: docs(conduit): clarify RPC connections, Rebase limits and benchmark data (@juicycleff)
- 946f198: docs(conduit): correct provider guarantees and PostgreSQL operations notes (@juicycleff)
- 7f49963: docs(conduit): document PostgreSQL and the providers at JetStream parity (@juicycleff)
- da0efc7: docs(conduit): fix the restore repair and provider wording (@juicycleff)
- 17d97a8: docs(conduit): list postgres among the configurable provider types (@juicycleff)
- 8bc75fd: docs(conduit): match the README's RPC selection to the memory rule (@juicycleff)
- 2f9a89a: docs(deploy): scope the ServiceMonitor cleanup command to one project (@juicycleff)
- 309aae1: docs(examples): point the observability example at forge obs (@juicycleff)
- aba276a: docs(examples): regenerate the observability dashboard and add alert rules (@juicycleff)
- 25a985f: docs(extensions): drop the advice to put false values in the config file (@juicycleff)
- eef42c4: docs(features): say flag values can come from YAML now (@juicycleff)
- 5f02880: docs(grpc): document gRPC tracing, rpc.server.call.duration and client dial options (@juicycleff)
- 1369316: docs(grpc): fix the interceptor order and note the observability edge cases (@juicycleff)
- 399b709: docs(metrics): add an upgrade note and say which trace settings turn metrics push on (@juicycleff)
- cda2f37: docs(metrics): document OTLP metrics push and drop StatsD and Influx (@juicycleff)
- 65919c9: docs(metrics): state the real config precedence and fix the cap and exemplar notes (@juicycleff)
- bba46b8: docs(metrics): tell Agent-free Datadog users that metrics push is on (@juicycleff)
- 6e8a822: docs(metrics): tie metrics push to app.Start and app.Stop, hedge exemplars, fix a table reference (@juicycleff)
- f0f9d09: docs(mqtt): deprecate the tracing flag MQTT 3.1.1 can't honour (@juicycleff)
- 1c49a49: docs(observability): cover boot and shutdown traces and the dashboard's OTel trace viewer (@juicycleff)
- 426d42c: docs(observability): fix the profiling link wording and cover pull, env and stream limits (@juicycleff)
- 681057c: docs(observability): lead the Grafana guide with forge dev --observability (@juicycleff)
- 35a2009: docs(observability): say span processors see request attributes in OnEnd (@juicycleff)
- 8b70e05: docs(observability): say that every span processor sees recorded unsampled spans (@juicycleff)
- 6778280: docs(router): describe the WebTransport stream limits in llm.txt (@juicycleff)
- b95b8b3: docs(telemetry): describe the metrics push lifecycle from Start to Stop in llm.txt (@juicycleff)
- 460c133: docs(telemetry): say that forge.New takes over the OpenTelemetry globals (@juicycleff)
- 2ce2d6c: docs(telemetry): say the server middleware sits in the router's outer slot (@juicycleff)
- c7421a7: docs(tracing): correct llm.txt for the forge sampler and add UnsampledSpanProcessor (@juicycleff)
- 2e2e59c: docs(webtransport): show the real setup and what each config field does (@juicycleff)
Build System
- 95179a1: build(deps): bump go-utils to v1.4.1 and confy to v1.1.0 in every module (@juicycleff)
- 18f7446: build(deps): require forge v1.12.9 in every module that replaces it locally (@juicycleff)
- bea17ec: build(graphql): build against this checkout's root like the other extensions (@juicycleff)
- aa75231: build: pin go-utils with span_id log context (@juicycleff)
- 6562d44: build: tidy sibling modules for the tracing dependencies (@juicycleff)
- 064d7df: build: tidy sub-module go.mod files for the pyroscope and OTLP metrics requirements (@juicycleff)
Other Changes
- b2033d0: chore(lint): clear the golangci-lint backlog in all 39 modules (@juicycleff)
- f876923: chore(lint): stop gosec flagging the Statfs block size on Linux (@juicycleff)
- 2314a6b: style(cache): separate final return statements (@juicycleff)
- 308f8d2: style(grpc): gofmt the lifecycles map in app_services_test.go (@juicycleff)
- 1e46bd7: style: fix lint findings left by the tracing work (@juicycleff)
- 8b589c8: test(cli): check the stack's files with promtool and run it end to end behind a gate (@juicycleff)
- 0bbcb7c: test(client): widen the backoff abort test so a slow runner can't fail it (@juicycleff)
- 5a3f241: test(conduit): add the backfill conformance group (@juicycleff)
- f9cbff6: test(conduit): add the operations conformance group (@juicycleff)
- c928f52: test(conduit): add the registry conformance group and configurable leases (@juicycleff)
- 4f9a0c3: test(conduit): add the rpc conformance group (@juicycleff)
- 35cd91d: test(conduit): benchmark PostgreSQL publish and delivery (@juicycleff)
- 13af966: test(conduit): fail the rpc group on a crossed reply (@juicycleff)
- 08f0064: test(conduit): give the PostgreSQL ordering tests the transaction IDs they need (@juicycleff)
- d56227a: test(conduit): keep the conformance groups from hanging on a failure (@juicycleff)
- c95f3a1: test(conduit): make the operations conformance group order-independent (@juicycleff)
- 0bb3a97: test(conduit): pin that an idle PostgreSQL subscriber writes nothing (@juicycleff)
- c5ed0ec: test(dashboard): restore the header allowlist and body capture checks (@juicycleff)
- 65d43f4: test(deploy): re-pin the atlas fixtures so go list can resolve them (@juicycleff)
- 81b4b09: test(metrics): benchmark observability per request and pin route parity (@juicycleff)
- 8d5434f: test(metrics): cover the OTLP collector's runtime families, namespace and error sentinel (@juicycleff)
- 919e8e8: test(metrics): fail the failed-start test when push never began (@juicycleff)
- 8c837c0: test(metrics): look for the root volume of the running OS in the system series test (@juicycleff)
- d4026f5: test(metrics): make the cut-short runtime test catch a prune (@juicycleff)
- ae3f11d: test(metrics): pin delta start times, third produces and the bucket clamp (@juicycleff)
- 0a94bed: test(router): bind the WebTransport test client to 127.0.0.1 (@juicycleff)
- 1205df9: test(router): drain raw bytes in the late-send WebSocket observer test (@juicycleff)
- 278967e: test(router): keep WebTransport sessions open until the client has its reply (@juicycleff)
- a07cbf9: test(telemetry): pin span_id across a handler's own pprof.Do on a matched route (@juicycleff)
- ad28472: test(telemetry): wait for the hijacking handler before reading its span (@juicycleff)
Installation
Using Go Install
go install github.com/xraph/forge/cmd/forge@v1.12.9Download Binary
Download the appropriate binary for your platform from the assets below.
Using Package Managers
# Homebrew (macOS/Linux)
brew install xraph/tap/forge
# Scoop (Windows)
scoop bucket add xraph https://github.com/xraph/scoop-bucket
scoop install forgeWhat's Changed
Full changelog: v1.12.8...v1.12.9