Skip to content

Services, External Services, and Projects

Every Entity Hexcovery observes falls into one of three classes, and the classification rests on a single OpenTelemetry attribute: service.namespace.

Classification is by namespace, NOT by traces

A common misconception is that "things with traces are services." That's wrong. Traces are just one signal. The class of an Entity is decided by whether its telemetry carries a service.namespace — nothing else.

The three classes

Class Rule Examples
Service Telemetry carries a service.namespace → it is attributed to a Project your Rails app, an auth microservice, even caddy (logs only)
External Service Seen only as a call-target, with no service.namespace → a dependency that belongs to no Project postgres, redis, smtp (until they gain an identity)
Infrastructure The hosts / Kubernetes nodes your software runs on — a separate substrate a VM, a k8s node

Two consequences worth internalizing:

  • caddy is a Service because it carries a service.namespace — even though it's third-party software you didn't write. postgres is an External Service because it has no namespace — even though it's "software" too. They do not sit together: the criterion is the namespace, full stop.
  • Traces are a facet, not the boundary. Having traces marks a Service as "instrumented by us / our own code" (your app is instrumented; caddy is external software you only collect logs from). That's a badge within the Services group — not the Service / External Service line.

How you make something a Service

The act of making a piece a Service of your Project is giving its telemetry a service.namespace. That attribute is the attribution mechanism.

A Project collects; it does not own

A Project is a service.namespace. It is an aggregation lens, not a container with ownership. It reunites and connects the entities that share its namespace to deliver:

  • at-a-glance health for everything in it, and
  • the "this error ← that piece" correlation.

The entities exist on their own first; the Project assembles them. The global, standalone screens (Services, Traces, Logs, metrics) remain the deep-dive tools — the Project is the hub that assembles, the tools explore.

Naming in the UI

A Project is literally a service.namespace. The left-nav may label it "Namespace Projects" to bridge the OTLP field you configure to the product word; it's just "Project" everywhere else. The pieces inside it are Services, which map exactly to service.name.

The namespace is the boundary (many-to-many, asymmetric)

The grouping relationships are intentionally lopsided:

  • A Service belongs to exactly one Project — its service.namespace is its home. It is never a native member of another Project.
  • A Project can hold several Services, all sharing that namespace.

    Example: Project SUPERPIPPO = { PIPPO (the main app) + PLUTO (the auth microservice) }.

  • An External Service has no home. With no namespace, it appears as a dependency inside every Project that calls it, shared among them.

  • A Service can still be referenced as a dependency by other Projects (a cross-Project call). There it shows as an edge that links back to its home Project.

    Example: PIPPO (home: SUPERPIPPO) is called over HTTP by a service in Project PAPERINIK → inside PAPERINIK it appears as a dependency edge that links to PIPPO's home.

\"Dependency\" is a relationship, not a type

A dependency is an edge between entities — not a fourth class of thing. The same Entity can be a first-class Service in its home Project and a dependency edge as seen from another.

Reconciliation: identity vs section

A call-target is observed as an edge keyed by server.address — the caller's spans don't carry the target's own service.namespace. Reconciliation maps that endpoint to a known Entity:

  • Reconciliation decides identity"which Entity is this endpoint?"
  • service.namespace decides the section — Service vs External Service.

So an edge that resolves to a namespaced Entity is shown as a Service (possibly cross-Project); an edge that resolves to nothing — or to something still without a namespace — stays an External Service. Reconciliation is automatic where possible (matching server.address against known identities), with a manual link as the fallback.

Graduation: an External Service becoming first-class

An External Service is edge-only — visible only as a dependency inside the Projects that call it — until it acquires its own identity. That typically happens when you attach its OTLP metrics (giving it a stable identity), or when you reconcile it to a namespaced Entity.

At that point it graduates into a Service: it gains its own home, a presence in the catalog, and a metrics lens of its own.

External Service                          Service
(dependency edge only)   ── graduates ─►  (own home + metrics lens)
   e.g. postgres, redis    via OTLP        e.g. a postgres with its
   seen via callers'       metrics          own postgresql receiver
   traces

This is exactly what attaching infrastructure metrics (the OTel receivers for PostgreSQL, Redis, Kafka, …) buys you: external software stops being an anonymous edge and becomes a first-class Entity you can observe on its own.

Global truth vs per-Project views

Every Entity has both:

  • a global truth — on its dedicated screen: all callers, all signals, overall health;
  • per-Project views — inside a Project, it's shown scoped to that Project's relationship with it (e.g. a shared database shown with the errors as seen from this Project's calls).

How the model is surfaced

  • By signal (one lens, all entities, filterable by Project): Traces, Logs, Cron, Metrics — plus Hosts / Kubernetes for the infra substrate. The deep-dive tools.
  • By Project (one Project, all its pieces + health): the Project hub, with sections Services · External Services · Infrastructure.
  • By entity (one entity, all its signals): the per-entity detail view — see Entities and signals.
  • The global Services catalog is presence-based across everything with a service.namespace. It's the discoverability front-door for people who don't yet think in Projects. External Services (no namespace) are not in it — they live inside Projects as edges until they graduate.

Where to go next