Skip to content

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 20:54
· 68 commits to main since this release
v0.7.0
21faed8

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 set service.namespace (logical domain), deployment.environment.name
    and avuru.tier as 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.tierdefaultTier, 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 (AEP design/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, logs and
    status read 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 json prints the raw API response, so anything the CLI does not yet model
    is still reachable with jq. 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, so sensor.obi.network.interZone.enabled turns 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 standard topology.kubernetes.io/zone label, 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 in tags.labels and 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=true rendered 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 a stats.enable key 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 was app → 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, and topology.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 a kubectl 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.