openapi: one emitter, not two — the typed registry is the document

o11y wrote the same server down twice. Emitter A is the typed op registry:
identity 45 + infra 44 + logs 9 + telemetry 5 = 103 ops registered on the
cloud.Router at their full public /v1/o11y/... paths, read from a real mount by
cloud's describe.go and woven into the one published document.

Emitter B was `o11y generate openapi`: a swaggest reflector walked the runtime's
gorilla/mux tree and wrote a committed 629 KB docs/api/openapi.yml — 120 paths,
174 ops, spelled /api/v1 x59, /api/v2 x57, /api/v3, /api/v4, /api/v5 x2, and
ZERO /v1/o11y. It described the fork's internal tree behind the delegation
wildcard's /v1/o11y/* -> /api/* rewrite: a surface no customer can address, in a
shape that breaks two house rules on its face (/api/, and v2 through v5). No CI
step regenerated or verified it, so it could only drift.

B dies whole: cmd/openapi.go, registerGenerateOpenAPI, pkg/o11y/openapi.go (the
reflector, unreachable once the command went) and the committed artifact.
pkg/http/handler's OpenAPIDef stays — it is per-handler prose the typed ops read,
not a second document.

Two scratch leaks from the same effort go with it:

  - zzz_surface_measure_test.go, self-labelled "TEMPORARY … not for commit",
    committed anyway and writing to a hardcoded /home/z/.cache path.
  - replace github.com/hanzoai/cloud => ../cloud. No CI runner has that sibling,
    so ci.yaml carried a second checkout of cloud at an exact commit to supply
    it — the same fact in two places, which is what LLM.md's own section warned
    about. The replace hid a broken pin, too: the required v1.801.218 no longer
    compiles against current hanzoai/tasks, and nothing noticed because CI never
    built the required version. go.mod now requires cloud v1.801.350 from the
    proxy like every other private hanzoai module; the checkout step is gone.

go build ./... clean; go test ./ ./cmd/... ./pkg/http/handler/... ./pkg/apiserver/...
./pkg/o11y/... all ok.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-01 11:20:15 -07:00
co-authored by Hanzo Dev
parent 56dea9dab5
commit d78ae42e2c
10 changed files with 736 additions and 21028 deletions
+3 -13
View File
@@ -17,24 +17,14 @@ jobs:
GOPRIVATE: github.com/hanzoai/*
GOSUMDB: "off"
steps:
# go.mod requires github.com/hanzoai/cloud at a released version and
# resolves it from the proxy like every other private hanzoai module — no
# sibling checkout, no second copy of the pin to keep in step with it.
- name: Checkout o11y
uses: actions/checkout@v4
with:
path: o11y
# go.mod pins `replace github.com/hanzoai/cloud => ../cloud` for local
# co-development, so CI supplies the sibling. Pin the exact commit o11y's
# module graph is reconciled against (readonly `go build ./...` is green
# against it) — cloud's `main` drifts independently and would break the
# readonly graph. Bump this when o11y's go.mod is re-tidied against cloud.
- name: Checkout hanzoai/cloud (sibling for replace => ../cloud)
uses: actions/checkout@v4
with:
repository: hanzoai/cloud
ref: ce6c4dc374160fae6873dd6ad6222bf52d803f71
token: ${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
path: cloud
- name: Set up Go
uses: actions/setup-go@v5
with:
+41 -22
View File
@@ -16,7 +16,6 @@ serving o11y.hanzo.ai. A clean full fork of **latest O11y** (synced to upstream
One way, and it runs on our own stack:
push -> github.com/hanzoai/o11y (a mirror)
.github/workflows/sync.yml carries refs onward
-> git.hanzo.ai/hanzoai/o11y CANONICAL
.hanzo/workflows/ci.yaml go build + go test
.hanzo/workflows/docker.yaml builds ghcr.io/hanzoai/o11y
@@ -25,11 +24,11 @@ One way, and it runs on our own stack:
-> hanzoai/operator reconciles the App
-> hanzoai/static behind hanzoai/ingress serves the SPA
**git.hanzo.ai is canonical; GitHub is a mirror.** `.github/workflows/` holds
exactly one file, `sync.yml`, and its only job is getting refs to the forge. Every
build, check and deploy is a workflow under `.hanzo/workflows/`, which the forge
reads. `.hanzo/workflows` uses GitHub Actions syntax, so a workflow moves between
the two by changing directory and nothing else.
**git.hanzo.ai is canonical; GitHub is a mirror.** Every build, check and deploy
is a workflow under `.hanzo/workflows/`, which the forge reads. `.hanzo/workflows`
uses GitHub Actions syntax, so a workflow moves between the two by changing
directory and nothing else. There is no `.github/workflows/` — refs reach the
forge by push, not by an Actions job.
No GitHub Pages and no Cloudflare Pages. `Dockerfile.site` already ends at
`FROM ghcr.io/hanzoai/static:v0.5.1` serving `frontend/build` on :3000, so the SPA
@@ -44,6 +43,27 @@ failure mode is the whole reason a migration is `git mv` and never a delete.
Also load-bearing via the **Go module tag**: `hanzoai/cloud` `go.mod` pins
`github.com/hanzoai/o11y`, because the query plane now lives in cloud at `/v1/o11y`.
## The document — ONE emitter, and it is the typed op registry
`identity.go` (45) + `infra.go` (44) + `logs.go` (9) + `telemetry.go` (5) = **103
typed ops**, registered on the `cloud.Router` this module is handed at their full
public `/v1/o11y/...` paths. Cloud's `describe.go` reads that registry from a real
mount and weaves the result into the one published document. Change a struct and
the document follows; there is nothing to keep in sync.
There was a second emitter and it is **deleted**: `cmd/openapi.go` ran a swaggest
reflector over the runtime's gorilla/mux tree and wrote a committed 629 KB
`docs/api/openapi.yml` — 120 paths, 174 ops, spelled `/api/v1` ×59, `/api/v2` ×57,
`/api/v3`, `/api/v4`, `/api/v5` ×2, and **zero** `/v1/o11y`. It described the
fork's internal tree behind the delegation wildcard's `/v1/o11y/* -> /api/*`
rewrite, so it documented a surface no customer can address, in a shape that
breaks two house rules on its face. No CI regenerated or verified it, so it could
only drift. Gone with it: `pkg/o11y/openapi.go` (the reflector — unreachable once
the command went) and `registerGenerateOpenAPI` from `cmd/generate.go`.
**If a route is not in the typed registry it is in no document.** That is the only
place to fix it; do not add a second writer.
## Dependency ownership (fork boundary)
All O11y-branded platform deps are OWNED as public `hanzoai/*` forks — never
@@ -73,25 +93,24 @@ consistent upstream version. Keep it that way: bump by re-syncing to a newer O11
The real server binary is `./cmd/community` (NOT `./cmd/server`, which does not
exist). Build check: `GOPRIVATE='github.com/hanzoai/*' GOSUMDB=off go build ./cmd/community`.
### go.mod is paired with the ci.yaml cloud pin — bump BOTH or CI goes red
### The cloud pin is ONE fact, in go.mod
Root `mount.go` imports `github.com/hanzoai/cloud`, and go.mod resolves it via
`replace => ../cloud`. There is no such sibling in CI, so `ci.yaml` checks one out
**at an exact commit** (`ref:` under "Checkout hanzoai/cloud") — deliberately, since
cloud's `main` drifts independently. That makes go.mod and the ci.yaml ref **ONE fact
in two places**: touch either alone and `go build ./...` dies with the misleading
`go: updates to go.mod needed; to update it: go mod tidy`. This kept CI red for weeks.
Root `mount.go` imports `github.com/hanzoai/cloud`, and go.mod requires it at a
**released version**, resolved from the proxy like every other private `hanzoai/*`
module (ci.yaml already configures git auth for exactly that). Bump it with
`go get github.com/hanzoai/[email protected]`.
To re-tidy: `go mod tidy` against the sibling, then **bump the ci.yaml `ref` to the
same cloud commit in the same PR**. Two traps:
It used to resolve via `replace => ../cloud`, which no CI runner has, so ci.yaml
carried a second checkout of cloud pinned to an exact commit to supply the
sibling — the same fact written twice, and touching either alone died with the
misleading `go: updates to go.mod needed`. Both the replace and the checkout step
are **deleted**; the version in go.mod is now the only place cloud is named. The
replace also hid a broken pin: the required version had drifted so far that a
standalone `go build ./...` could not compile cloud at all, and nothing noticed
because CI never built the required version.
- Tidy resolves against **whatever `../cloud` is checked out right now** — that clone
is a working copy, often on someone's feature branch, and it moves under you. Derive
versions from the commit you are pinning (`git -C ../cloud show <sha>:go.mod`), not
from the branch that happens to be there. Same class of hazard as the stale luxfi
clones.
- The resulting version jumps are **not upgrade decisions** — MVS forces them, because
cloud already requires them. Do not "fix" them back down; re-pair the two sides.
The version jumps a bump drags in are **not upgrade decisions** — MVS forces them,
because cloud already requires them. Do not "fix" them back down.
Since a green build is NOT sufficient evidence (`hanzoai/zip` → `zap-proto/zip` can
boot-panic while CI stays green), smoke-test the binary: it must reach
-1
View File
@@ -15,7 +15,6 @@ func RegisterGenerate(parentCmd *cobra.Command, logger *slog.Logger) {
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
}
registerGenerateOpenAPI(generateCmd)
registerGenerateAuthz(generateCmd)
registerGenerateConfig(generateCmd)
-41
View File
@@ -1,41 +0,0 @@
package cmd
import (
"context"
"log/slog"
"github.com/hanzoai/o11y/pkg/instrumentation"
"github.com/hanzoai/o11y/pkg/o11y"
"github.com/hanzoai/o11y/pkg/version"
"github.com/spf13/cobra"
)
func registerGenerateOpenAPI(parentCmd *cobra.Command) {
openapiCmd := &cobra.Command{
Use: "openapi",
Short: "Generate OpenAPI schema for O11y",
RunE: func(currCmd *cobra.Command, args []string) error {
return runGenerateOpenAPI(currCmd.Context())
},
}
parentCmd.AddCommand(openapiCmd)
}
func runGenerateOpenAPI(ctx context.Context) error {
instrumentation, err := instrumentation.New(ctx, instrumentation.Config{Logs: instrumentation.LogsConfig{Level: slog.LevelInfo}}, version.Info, "o11y")
if err != nil {
return err
}
openapi, err := o11y.NewOpenAPI(ctx, instrumentation)
if err != nil {
return err
}
if err := openapi.CreateAndWrite("docs/api/openapi.yml"); err != nil {
return err
}
return nil
}
-20637
View File
File diff suppressed because it is too large Load Diff
+148 -3
View File
@@ -25,7 +25,7 @@ require (
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/hanzo-ds/go v1.0.1
github.com/hanzoai/cloud v1.801.218
github.com/hanzoai/cloud v1.801.350
github.com/hanzoai/govaluate v0.1.0
github.com/hanzoai/otel-collector v1.2.0
github.com/hanzoai/sqlite v0.4.0
@@ -94,33 +94,93 @@ require (
)
require (
cloud.google.com/go v0.123.0 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/ThinkInAIXYZ/go-mcp v0.2.24 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-openapi v0.1.18 // indirect
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.16 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/ecs-20140526/v4 v4.26.10 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/openapi-util v0.1.2 // indirect
github.com/alibabacloud-go/resourcecenter-20221201 v1.5.1 // indirect
github.com/alibabacloud-go/tea v1.4.0 // indirect
github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.9 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/alibabacloud-go/vod-20170321/v2 v2.16.10 // indirect
github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.3.0 // indirect
github.com/aliyun/credentials-go v1.4.7 // indirect
github.com/anthropics/anthropic-sdk-go v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ec2 v1.296.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sns v1.39.11 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
github.com/aws/smithy-go v1.24.2 // indirect
github.com/baidubce/bce-qianfan-sdk/go/qianfan v0.0.14 // indirect
github.com/baidubce/bce-sdk-go v0.9.264 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/buger/goterm v1.0.4 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/bytedance/sonic/loader v0.5.1 // indirect
github.com/carmel/gooxml v0.0.0-20220216072414-40ff56130850 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/cohere-ai/cohere-go/v2 v2.5.2 // indirect
github.com/consensys/gnark-crypto v0.20.1 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
github.com/deckarep/golang-set/v2 v2.9.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/digitalocean/go-libvirt v0.0.0-20260217163227-273eaa321819 // indirect
github.com/diskfs/go-diskfs v1.9.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/djherbis/times v1.6.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/docker/docker v28.5.2+incompatible // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.7 // indirect
github.com/fasthttp/websocket v1.5.12 // indirect
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gage-technologies/mistral-go v1.1.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.9.0 // indirect
github.com/go-git/go-git/v5 v5.19.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
github.com/go-openapi/swag/conv v0.25.5 // indirect
@@ -135,66 +195,144 @@ require (
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sql-driver/mysql v1.10.0 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gofiber/schema v1.7.1 // indirect
github.com/gofiber/utils/v2 v2.0.4 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/gorilla/rpc v1.2.1 // indirect
github.com/grandcat/zeroconf v1.0.0 // indirect
github.com/hanzo-ds/native v0.72.0 // indirect
github.com/hanzoai/account v0.2.0 // indirect
github.com/hanzoai/ai v1.832.5 // indirect
github.com/hanzoai/authz v1.10.29 // indirect
github.com/hanzoai/commerce v1.49.36 // indirect
github.com/hanzoai/csqlite v0.1.0 // indirect
github.com/hanzoai/dashscope-go-sdk v0.0.2 // indirect
github.com/hanzoai/dashscopego v0.6.0 // indirect
github.com/hanzoai/dbx v1.17.2 // indirect
github.com/hanzoai/decimal v0.1.1 // indirect
github.com/hanzoai/go-cosyvoice v1.0.0 // indirect
github.com/hanzoai/go-openai v1.41.0 // indirect
github.com/hanzoai/go-openai-realtime v1.0.0 // indirect
github.com/hanzoai/go-openai-realtime/contrib/ws-gorilla v1.0.0 // indirect
github.com/hanzoai/go-openrouter v1.0.0 // indirect
github.com/hanzoai/ha v0.1.1 // indirect
github.com/hanzoai/iam v1.33.26 // indirect
github.com/hanzoai/metrics v1.110.2 // indirect
github.com/hanzoai/money v0.2.1 // indirect
github.com/hanzoai/orm v0.6.18 // indirect
github.com/hanzoai/pdf v1.2.0 // indirect
github.com/hanzoai/s3-go v1.0.0 // indirect
github.com/hanzoai/search-go v0.36.0 // indirect
github.com/hanzoai/sqlcipher v0.1.1 // indirect
github.com/hanzoai/tasks v1.52.4 // indirect
github.com/hanzoai/vfs v0.6.6 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/henomis/lingoose v0.1.0 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.0 // indirect
github.com/hhrutter/tiff v1.0.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/huandu/go-clone v1.7.3 // indirect
github.com/hupe1980/go-huggingface v0.0.15 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/leverly/ChatGLM v1.2.0 // indirect
github.com/lib/pq v1.12.3 // indirect
github.com/lithammer/shortuuid v3.0.0+incompatible // indirect
github.com/luthermonson/go-proxmox v0.4.0 // indirect
github.com/luxfi/accel v1.2.4 // indirect
github.com/luxfi/cache v1.3.1 // indirect
github.com/luxfi/container v0.2.1 // indirect
github.com/luxfi/crypto v1.20.2 // indirect
github.com/luxfi/crypto/ipa v1.2.4 // indirect
github.com/luxfi/geth v1.20.1 // indirect
github.com/luxfi/ids v1.3.2 // indirect
github.com/luxfi/math v1.5.1 // indirect
github.com/luxfi/math/big v0.1.0 // indirect
github.com/luxfi/mdns v0.1.1 // indirect
github.com/luxfi/metric v1.9.0 // indirect
github.com/luxfi/mock v0.1.1 // indirect
github.com/luxfi/pq v1.1.0 // indirect
github.com/luxfi/trace v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/microsoft/go-mssqldb v1.9.5 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/mr-tron/base58 v1.3.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/openai/openai-go/v2 v2.1.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect
github.com/oschwald/geoip2-golang v1.11.0 // indirect
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
github.com/pdfcpu/pdfcpu v0.11.0 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pkoukk/tiktoken-go v0.1.7 // indirect
github.com/prometheus/client_golang/exp v0.0.0-20260325093428-d8591d0db856 // indirect
github.com/puzpuzpuz/xsync/v4 v4.4.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.16 // indirect
github.com/swaggest/refl v1.4.0 // indirect
github.com/swaggest/usecase v1.3.1 // indirect
github.com/tealeg/xlsx v1.0.5 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.77 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.1116 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/hunyuan v1.3.48 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbaas v1.1.13 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/tinylib/msgp v1.6.4 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ua-parser/uap-go v0.0.0-20251207011819-db9adb27a0b8 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.72.0 // indirect
github.com/volcengine/volc-sdk-golang v1.0.241 // indirect
github.com/volcengine/volcengine-go-sdk v1.0.141 // indirect
github.com/wangbin/jiebago v0.3.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/zap-proto/fiber/v3 v3.2.1 // indirect
github.com/zap-proto/go v1.3.0 // indirect
github.com/zap-proto/http v0.3.1 // indirect
github.com/zap-proto/md v0.1.0 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect
go.mongodb.org/mongo-driver v1.17.9 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.opentelemetry.io/collector/client v1.54.0 // indirect
go.opentelemetry.io/collector/config/configoptional v1.50.0 // indirect
@@ -205,16 +343,25 @@ require (
go.uber.org/goleak v1.3.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
golang.org/x/arch v0.25.0 // indirect
golang.org/x/image v0.41.0 // indirect
golang.org/x/term v0.45.0 // indirect
google.golang.org/genai v1.10.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
k8s.io/api v0.36.2 // indirect
k8s.io/kube-openapi v0.0.0-20260330154417-16be699c7b31 // indirect
k8s.io/metrics v0.36.1 // indirect
modernc.org/libc v1.72.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.51.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.21.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
@@ -427,5 +574,3 @@ require (
)
replace github.com/expr-lang/expr => github.com/hanzoai/expr v1.17.8
replace github.com/hanzoai/cloud => ../cloud
+542
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -52,8 +52,8 @@ func TestLLMObsRoutes(t *testing.T) {
}
}
// Reflect every registered route's OpenAPI definition; a bad request/response
// DTO would surface here exactly as it would in the spec generator.
// Reflect every registered route's OpenAPI definition: a request or response
// DTO the reflector cannot describe is a DTO no typed op can carry either.
collector := handler.NewOpenAPICollector(openapi3.NewReflector())
if err := router.Walk(collector.Walker); err != nil {
t.Fatalf("openapi reflection failed: %v", err)
-271
View File
@@ -1,271 +0,0 @@
package o11y
import (
"bytes"
"context"
"encoding/json"
"os"
"reflect"
"github.com/hanzoai/o11y/pkg/alertmanager"
"github.com/hanzoai/o11y/pkg/apiserver"
"github.com/hanzoai/o11y/pkg/apiserver/o11yapiserver"
"github.com/hanzoai/o11y/pkg/authz"
"github.com/hanzoai/o11y/pkg/factory"
"github.com/hanzoai/o11y/pkg/flagger"
"github.com/hanzoai/o11y/pkg/gateway"
"github.com/hanzoai/o11y/pkg/global"
"github.com/hanzoai/o11y/pkg/http/handler"
"github.com/hanzoai/o11y/pkg/instrumentation"
"github.com/hanzoai/o11y/pkg/modules/authdomain"
"github.com/hanzoai/o11y/pkg/modules/cloudintegration"
"github.com/hanzoai/o11y/pkg/modules/dashboard"
"github.com/hanzoai/o11y/pkg/modules/errortracking"
"github.com/hanzoai/o11y/pkg/modules/fields"
"github.com/hanzoai/o11y/pkg/modules/inframonitoring"
"github.com/hanzoai/o11y/pkg/modules/llmobs"
"github.com/hanzoai/o11y/pkg/modules/llmpricingrule"
"github.com/hanzoai/o11y/pkg/modules/metricreductionrule"
"github.com/hanzoai/o11y/pkg/modules/metricsexplorer"
"github.com/hanzoai/o11y/pkg/modules/organization"
"github.com/hanzoai/o11y/pkg/modules/preference"
"github.com/hanzoai/o11y/pkg/modules/promote"
"github.com/hanzoai/o11y/pkg/modules/rawdataexport"
"github.com/hanzoai/o11y/pkg/modules/rulestatehistory"
"github.com/hanzoai/o11y/pkg/modules/sentry"
"github.com/hanzoai/o11y/pkg/modules/serviceaccount"
"github.com/hanzoai/o11y/pkg/modules/session"
"github.com/hanzoai/o11y/pkg/modules/spanmapper"
"github.com/hanzoai/o11y/pkg/modules/tracedetail"
"github.com/hanzoai/o11y/pkg/modules/user"
"github.com/hanzoai/o11y/pkg/querier"
"github.com/hanzoai/o11y/pkg/ruler"
"github.com/hanzoai/o11y/pkg/statsreporter"
"github.com/hanzoai/o11y/pkg/types/authtypes"
"github.com/hanzoai/o11y/pkg/zeus"
"github.com/swaggest/jsonschema-go"
"github.com/swaggest/openapi-go"
"github.com/swaggest/openapi-go/openapi3"
"gopkg.in/yaml.v2"
)
const o11yDiscriminatorKey string = "x-o11y-discriminator"
type OpenAPI struct {
apiserver apiserver.APIServer
reflector *openapi3.Reflector
collector *handler.OpenAPICollector
}
func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumentation) (*OpenAPI, error) {
apiserver, err := o11yapiserver.NewFactory(
struct{ organization.Getter }{},
struct{ authz.AuthZ }{},
struct{ organization.Handler }{},
struct{ user.Handler }{},
struct{ session.Handler }{},
struct{ authdomain.Handler }{},
struct{ preference.Handler }{},
struct{ global.Handler }{},
struct{ promote.Handler }{},
struct{ flagger.Handler }{},
struct{ dashboard.Module }{},
struct{ dashboard.Handler }{},
struct{ metricsexplorer.Handler }{},
struct{ metricreductionrule.Handler }{},
struct{ inframonitoring.Handler }{},
struct{ gateway.Handler }{},
struct{ fields.Handler }{},
struct{ authz.Handler }{},
struct{ rawdataexport.Handler }{},
struct{ zeus.Handler }{},
struct{ querier.Handler }{},
struct{ serviceaccount.Handler }{},
struct{ factory.Handler }{},
struct{ cloudintegration.Handler }{},
struct{ rulestatehistory.Handler }{},
struct{ spanmapper.Handler }{},
struct{ alertmanager.Handler }{},
struct{ llmpricingrule.Handler }{},
struct{ tracedetail.Handler }{},
struct{ ruler.Handler }{},
struct{ statsreporter.Handler }{},
struct{ llmobs.Handler }{},
struct{ errortracking.Handler }{},
struct{ sentry.Handler }{},
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
if err != nil {
return nil, err
}
reflector := openapi3.NewReflector()
reflector.JSONSchemaReflector().DefaultOptions = append(reflector.JSONSchemaReflector().DefaultOptions, jsonschema.InterceptDefName(func(t reflect.Type, defaultDefName string) string {
if defaultDefName == "RenderSuccessResponse" {
field, ok := t.FieldByName("Data")
if !ok {
return defaultDefName
}
return field.Type.Name()
}
return defaultDefName
}))
reflector.Spec.WithInfo(*(&openapi3.Info{}).
WithTitle("O11y").
WithDescription("OpenTelemetry-Native Logs, Metrics and Traces in a single pane").
WithTermsOfService("https://o11y.io/terms-of-service/").
WithContact(*(&openapi3.Contact{}).
WithName("O11y Support").
WithURL("https://o11y.io").
WithEmail("[email protected]")),
)
reflector.Spec.WithServers(
// Default server
*(&openapi3.Server{}).WithURL("https://{host}:{port}{base_path}").
WithDescription("The fully qualified URL to the O11y APIServer.").
WithVariablesItem("host", *(&openapi3.ServerVariable{}).
WithDefault("localhost").
WithDescription("The host of the O11y APIServer")).
WithVariablesItem("port", *(&openapi3.ServerVariable{}).
WithDefault("8080").
WithDescription("The port of the O11y APIServer")).
WithVariablesItem("base_path", *(&openapi3.ServerVariable{}).
WithDefault("/").
WithDescription("The base path of the O11y APIServer")),
)
reflector.SpecSchema().SetAPIKeySecurity(authtypes.IdentNProviderAPIKey.StringValue(), "O11y-Api-Key", openapi.InHeader, "API Keys")
reflector.SpecSchema().SetHTTPBearerTokenSecurity(authtypes.IdentNProviderTokenizer.StringValue(), "Tokenizer", "Tokens generated by the tokenizer")
collector := handler.NewOpenAPICollector(reflector)
return &OpenAPI{
apiserver: apiserver,
reflector: reflector,
collector: collector,
}, nil
}
func (openapi *OpenAPI) CreateAndWrite(path string) error {
if err := openapi.apiserver.Router().Walk(openapi.collector.Walker); err != nil {
return err
}
attachDiscriminators(openapi.reflector.Spec)
// The library's MarshalYAML does a JSON round-trip that converts all numbers
// to float64, causing large integers (e.g. epoch millisecond timestamps) to
// render in scientific notation (1.6409952e+12).
jsonData, err := openapi.reflector.Spec.MarshalJSON()
if err != nil {
return err
}
dec := json.NewDecoder(bytes.NewReader(jsonData))
dec.UseNumber()
var v any
if err := dec.Decode(&v); err != nil {
return err
}
convertJSONNumbers(v)
spec, err := yaml.Marshal(v)
if err != nil {
return err
}
return os.WriteFile(path, spec, 0o600)
}
// convertJSONNumbers recursively walks a decoded JSON structure and converts
// json.Number values to int64 (preferred) or float64 so that YAML marshaling
// renders them as plain numbers instead of quoted strings.
func convertJSONNumbers(v interface{}) {
switch val := v.(type) {
case map[string]interface{}:
for k, elem := range val {
if n, ok := elem.(json.Number); ok {
if i, err := n.Int64(); err == nil {
val[k] = i
} else if f, err := n.Float64(); err == nil {
val[k] = f
}
} else {
convertJSONNumbers(elem)
}
}
case []interface{}:
for i, elem := range val {
if n, ok := elem.(json.Number); ok {
if i64, err := n.Int64(); err == nil {
val[i] = i64
} else if f, err := n.Float64(); err == nil {
val[i] = f
}
} else {
convertJSONNumbers(elem)
}
}
}
}
// attachDiscriminators promotes x-o11y-discriminator extensions
// into openapi3 Discriminator fields. Malformed markers are dropped.
func attachDiscriminators(spec *openapi3.Spec) {
if spec.Components == nil || spec.Components.Schemas == nil {
return
}
for name, entry := range spec.Components.Schemas.MapOfSchemaOrRefValues {
if entry.Schema == nil {
continue
}
raw, ok := entry.Schema.MapOfAnything[o11yDiscriminatorKey]
if !ok {
continue
}
marker, ok := raw.(map[string]any)
if !ok {
continue
}
propertyName, ok := marker["propertyName"].(string)
if !ok || propertyName == "" {
continue
}
disc := openapi3.Discriminator{PropertyName: propertyName}
if rawMapping, ok := marker["mapping"]; ok {
if mapping, ok := rawMapping.(map[string]string); ok {
disc.Mapping = mapping
} else if mapping, ok := rawMapping.(map[string]any); ok {
converted := make(map[string]string, len(mapping))
for k, v := range mapping {
if s, ok := v.(string); ok {
converted[k] = s
}
}
disc.Mapping = converted
}
}
entry.Schema.Discriminator = &disc
delete(entry.Schema.MapOfAnything, o11yDiscriminatorKey)
// The parent's reflected `properties` / `required` duplicate
// what the oneOf variants already declare, and orval intersects
// the two — turning a clean discriminated union DTO into a
// noisy union of intersections. Drop them here.
entry.Schema.Properties = nil
entry.Schema.Required = nil
spec.Components.Schemas.MapOfSchemaOrRefValues[name] = entry
}
}
-38
View File
@@ -1,38 +0,0 @@
package o11y
// TEMPORARY measurement harness (scratch; not for commit).
import (
"encoding/json"
"os"
"testing"
"github.com/hanzoai/cloud"
"github.com/zap-proto/zip"
)
func TestZZZMeasureSurface(t *testing.T) {
dir, _ := os.MkdirTemp("", "surface-*")
cfg := &cloud.Config{Brand: "hanzo", Domain: "api.hanzo.ai", DataDir: dir}
deps := cloud.BuildDeps(cfg)
app := zip.New(zip.Config{Logger: deps.Logger, DisableStartupMessage: true})
if err := Mount(app, deps); err != nil {
t.Fatalf("Mount: %v", err)
}
type R struct{ Method, Path string }
var routes []R
zipOpenAPI, zipMCP := false, false
for _, r := range app.Fiber().GetRoutes(true) {
routes = append(routes, R{r.Method, r.Path})
switch r.Path {
case "/.well-known/openapi.json", "/openapi.json":
zipOpenAPI = true
case "/mcp":
zipMCP = true
}
}
b, _ := json.Marshal(map[string]any{"service": "o11y", "live_routes": len(routes),
"zip_openapi_route": zipOpenAPI, "zip_mcp_route": zipMCP, "routes": routes})
os.WriteFile("/home/z/.cache/wf-typedsurface/o11y_live.json", b, 0o644)
t.Logf("SVC=o11y LIVE=%d ZIP_OPENAPI=%v ZIP_MCP=%v", len(routes), zipOpenAPI, zipMCP)
}