v0.7.0
The clients and the labels. v0.6 opened both ends of the pipe. What arrived
was only as useful as the words it could be filed under and the surfaces that
could read it — and, it turned out, as truthful as the map drawing it.
So v0.7 does three things. It lets telemetry be sliced by the vocabulary your
organisation already uses: map a Kubernetes label once and it rides every
signal, or let a service declare its own domain, environment and tier and be
grouped accordingly with no hub config. It adds two more clients — a CLI that
can fail a deploy on a predicate, and a Grafana data source that reads Avuru Obs
from dashboards you already run — plus a link to the manual on every screen. And
it fixes what the map was telling you: on a meshed cluster every application
call was drawn as two hops through a proxy, and turning on kernel network flows
had been rendering a sensor config the eBPF tracer refuses to parse, so the
container never started at all.
Added
-
Every screen links to the page of the manual that explains it. A small
docs ↗beside the breadcrumb, because the question "what am I looking at?"
is asked while looking at the screen, not from a help menu two clicks away.
The link comes from the same navigation model the sidebar and breadcrumbs
already use, so a new screen gets one by declaring where its documentation
lives — and a screen with no page yet renders nothing at all, since a link
that 404s is worse than no link. -
Declared service metadata — services group and tier themselves. A service
can now setservice.namespace(logical domain),deployment.environment.name
andavuru.tieras resource attributes, and the service-health board picks
them up with no hub config: groups span Kubernetes namespaces, and one domain
declared in two environments becomes two groups carrying their own tiers.
Declaring nothing is unchanged — services still auto-group by namespace at
serviceGroups.defaultTier. Tier precedence is
serviceGroups.tierOverrides→ a matched config group → the declared
avuru.tier→defaultTier, and where group members disagree the most
critical tier wins. An invalid declared tier never fails the hub: it falls
back to the default and the board says so, in a banner naming the service and
the value it tried to use — the opposite of operator config, which still fails
loud. Failing soft is right (application telemetry gets no operator review)
but silence is not: without the banner a team would never learn their
declaration did nothing. A card also says declared when the service chose
its own tier, so a T0 lane distinguishes what ops decided from what an app
claimed (AEPdesign/2026-07-28-declared-service-metadata.md). -
serviceGroups.tierOverrides— per-service operator tier, winning over
both a declared tier and a matched group's tier. Corrects one service's
criticality without moving it into a different group. -
avuruobs, a command-line client. The Hub API was always meant to be the
contract and the web app one client of it; this is the second client, for the
places a browser cannot go.avuruobs services,health,traces,logsand
statusread the same API the UI does, authenticated with a personal API
token that resolves to its owner's live permissions — so the CLI sees exactly
what that person sees, with no second authorization model to keep in sync.The reason to have it is
--fail-on:avuruobs health --fail-on 'status!=healthy'exits 2 when the predicate matches, 1 when the
command itself failed, and 0 when nothing matched. Those are three
different things and a deploy gate needs to tell them apart — with a single
non-zero exit, an expired token returns no rows and the gate reads "nothing
over threshold" as success. For the same reason, a predicate naming a field no
row carries is an error rather than a pass.-o jsonprints the raw API response, so anything the CLI does not yet model
is still reachable withjq. Static binaries for Linux, macOS and Windows on
both architectures are attached to each release with checksums, and the binary
has no third-party dependencies — a tool you hand an API token should have
a supply chain you can read in an afternoon. -
A Grafana data source. Teams live in Grafana; this brings Avuru Obs into
the dashboards they already run — service RED metrics, service health, trace
search and cross-zone traffic, through the public Hub API. The panel's time
range is the query's time range, and a panel can name its own project, so one
dashboard can show two environments side by side.It is a backend plugin, and two things follow from that: the API token is
held in Grafana's encrypted settings and decrypted only inside the plugin's own
process, so it never reaches a browser — and queries leave the Grafana server
rather than the viewer's machine, so a hub reachable only inside the cluster
works without exposing it. Save & test calls the same endpoint every query
goes through, so a green check means the credential works, not merely that
something answered on that host.A token resolves to its owner's live permissions, so the data source sees
exactly what that person sees. An example dashboard ships with it. The plugin
is not signed by Grafana Labs — signing means publishing through their
catalogue, which is a separate step — so it currently loads as an unsigned
plugin; the README says how, and why. -
Cross-zone traffic accounting. Cloud providers bill data that crosses an
availability-zone boundary, and the usual way to find out what is driving that
line is a flow-log pipeline or a cost SaaS. The sensor already watches every
connection in the kernel, sosensor.obi.network.interZone.enabledturns that
into a byte matrix per zone pair —eu-west-1a → eu-west-1b, both directions
counted separately, on the Dashboard's capacity band and at
GET /api/v1/network/zones. Same-zone traffic is never counted and no
per-workload data is involved: the number of series is the number of zone
pairs, so this stays cheap on a cluster where per-workload flow metrics would
not. It works on its own — it does not require the per-edge network
feature, so a cluster can have its bill explained without paying for the full
flow topology. Off by default; needs the infra-metrics module, nodes carrying
the standardtopology.kubernetes.io/zonelabel, and (like the flow feature)
host networking on the sensor pod. -
Slice telemetry by the words your organisation already uses. A cluster
encodes ownership in labels —team=payments,tier=critical,
cost-center=…— and until now none of it reached the product: you could
group by service and by namespace, and that was the vocabulary. Map a label
once intags.labelsand it is carried onto every signal the platform
collects as a first-class tag, then offered as a filter on the traces and logs
screens. No application changes anything: the mapping is applied where
telemetry is collected, which means the workloads you never instrumented are
tagged too. A trace matches when any service that took part carries the
tag, not only the one that started it, so filtering by a team shows the
requests that reached it rather than only the ones it began. Discovered keys
and their values are offered as controls, so nobody has to remember what the
cluster is labelled with, and both screens share one filter string — a link
carries between them. Deliberately bounded: every mapped tag becomes a
dimension on the metrics it touches, so the chart refuses more than twelve and
the values documentation says to map identity, never per-pod detail.
Fixed
-
Turning on network flows stopped the sensor instead of enriching it.
sensor.obi.network.enabled=truerendered a sensor config with the same
attributes:key twice — once for Kubernetes decoration, once for the
TCP-stats attribute selection. Kubernetes rejects nothing here (it is a
ConfigMap value, not a manifest), but the sensor parses that document
strictly and refuses to start on a repeated key, so the whole eBPF container
crash-looped and took traces, flows and per-edge health with it. Both blocks
now render into one mapping. In the same config, the TCP-stats switch was
written as astats.enablekey the sensor has no field for: it was accepted
and ignored, so per-edge RTT and failed-connection metrics were never
collected even when the config did load. Metric families are now selected by
the sensor's own feature names, which is the only switch that exists for
them. A third key,network.allowed_attributes, was likewise not a field the
sensor has — so the cardinality bound the chart documented was never applied.
Left to their defaults, flow metrics also carry traffic direction and
per-interface labels, and the TCP-stats metrics carry source and destination
IP addresses, which is a time series per address pair. All three metrics
are now pinned to the Kubernetes workload identity the service map joins on.
Per-edge network health has therefore never worked on a real cluster since it
shipped in v0.2.0 — if you enabled it, it is worth re-checking the sensor pod.
Rendered configs are now parsed in the chart's own tests, so a document the
sensor cannot load fails the build. -
The service map no longer draws mesh hops as dependencies. On a cluster
running a service mesh, every application call is intercepted by a proxy, so
what reached the map wasapp → proxy → app— two edges, neither of them a
dependency, joining services that never talk to each other. The hub now
recognises transport workloads (mesh sidecars, waypoint and ztunnel proxies,
ingress and egress gateways for Istio, Linkerd, Consul, Kuma and Envoy
Gateway) and the map hides them by default, with a Show mesh & gateways
toggle when you want to see the plumbing. The classification is a name-match
and therefore install-specific, so it is configurable:topology.transport
adds patterns for a mesh the built-ins don't know, andtopology.applications
rescues a real service the built-ins catch. It ships on every install (the map
is core, so its correction knob has to be), lives in a ConfigMap the hub
hot-reloads, and applies within ~15s of akubectl edit— no restart, no
upgrade. Deliberately narrow by default: erasing a real service from the map
is a worse failure than the noise this removes. -
The map counted connections as calls. Edges derived from kernel flows
carry no call volume by construction, so a map showing "5 call edges" could
have observed no calls at all. The count line now separates traced calls from
observed network flows, a flow-only edge is drawn dotted rather than as a thin
call, and hovering one says "network flow · no traced calls" instead of
claiming 0 rpm and 0 ms.