Connect a cluster
Open Discovery → Connect cluster as an editor or admin. Choose a name, 1–20 existing namespaces (pasting more than 20 shows an error naming how many to remove; none are dropped), and an optional private location. Enter the HTTPS API origin and the collector image: the form prefills the published repository, ghcr.io/yassinebenameur/probara-discovery-collector:, and you add your release's version as the tag (or replace it with an image you built and published yourself). The browser remembers the image you last downloaded a manifest with: a published image comes back without its tag, so each install names the release it runs, while an image of your own is offered again as is. Download the manifest and apply it to the intended cluster. The API requires migration 000098. The collector runs in the first namespace you enter, the integration's home namespace (home_namespace), which stays fixed; the others can be changed later. It lists Services and Ingresses every five minutes.
The manifest contains namespaced list-only Roles for every configured namespace, a ServiceAccount, a non-root Deployment, and a Secret with an integration-scoped token. Kubernetes credentials stay in the cluster. The collector does not use a tenant API key, NATS, or PostgreSQL. It proposes checks; existing workers execute them after approval. Private targets require a separately deployed location worker with the necessary network access.
The collector trusts the public CA roots in its image. When the Probara origin uses a certificate from an internal CA, put that CA in a ConfigMap in the collector's namespace, mount it into the Deployment, and set SSL_CERT_FILE to the mounted file (see collector settings). Reapply this change whenever you apply a regenerated manifest.
# Only when the published image is not available:
docker build -f cmd/discovery-collector/Dockerfile -t registry.example.com/probara-discovery:discovery-v1 .
# Publish to your registry through your usual process, then use that image in setup.
kubectl --context your-cluster apply -f probara-discovery-<integration-prefix>.yamlReview suggestions
| Resource | Suggestion |
|---|---|
| Ingress with a concrete hostname | HTTP GET /; HTTPS when spec.tls declares the hostname or a wildcard covering it (*.example.com covers api.example.com). Wildcard and catch-all rules need manual configuration. |
| Service TCP port | TCP connectivity from a private location. This does not prove authenticated application health. |
| Service with probara.io/health-path | HTTP health check. Specify probara.io/health-port for a multi-port service and optional probara.io/health-scheme: https. Invalid or oversized health hints fall back to TCP without blocking inventory scans. |
| UDP or ExternalName Service | Inventory only; no automatic check suggestion. |
Set probara.io/discover: false to exclude a resource. Set probara.io/service to the same name on resources in one namespace to group their suggestions under one service. Grouping values are trimmed; values over 100 bytes are ignored. Otherwise identity is namespace/kind/name within the integration. Changing this grouping does not silently move existing monitor links: the old service keeps its links and goes missing (with the usual 15-minute grace), and an old service with no links is removed at once. Its expanded row says its resources moved; link the monitors to the new service and unlink them from the old one. Reverting the grouping within 7 days moves the resources back to the kept service and clears its missing state.
Existing monitors of the same target are candidates for explicit linking — matching ignores host case, a trailing root slash, an explicit default port, and the .cluster.local suffix of a Service name. Verify their assertions and location. Approving a target that an existing monitor already checks from the same location is refused; link that monitor instead. Approval selects a name, interval, execution location, expected HTTP status, and workspace-default or specific active notification channels. Monitor creation, its initial state interval, assignments, and inventory link are atomic. Repeated approval returns the linked monitor with created: false; only the approval that created it is audited. Customize other assertions in the normal monitor editor afterward.
Change namespaces
Edit cluster (the pencil in the cluster header, editors and admins) renames the integration, changes its default private location, and adds or removes namespaces, at most 20 active and pending together. The home namespace is locked: the collector's Deployment, ServiceAccount and Secret live there, so moving the collector means connecting a new integration and linking existing monitors to it. A namespace change goes through a review that states what each change does, with the number of services affected and how many of them are linked to monitors, and takes effect at the next scan (at most five minutes; the collector reads its configuration every cycle and needs no restart).
An added namespace is pending until the collector can read it; nothing else changes meanwhile. Each cycle scans the active namespaces first, then each pending one all-or-nothing: a failure only skips that namespace, and every other namespace keeps syncing. The first snapshot that scans it in full makes it active, and its services join coverage. Until then its pill reads "waiting for first scan" or the skip reason, and a warning on the page offers the access manifest. An active namespace's pill shows its service count, and one still empty after a scan is flagged "no services" (a misspelled name under cluster-wide RBAC scans fine and finds nothing).
| Skip reason | Meaning |
|---|---|
forbidden | Kubernetes answered 401 or 403: the namespace does not exist or the access manifest is not applied there. |
too_large | A list page over 4 MiB, or the namespace would take the scan past 2,000 resources or the uploaded snapshot past 4 MiB. Pending namespaces are fitted into the upload in scan order, so a smaller one after it can still be promoted. |
inventory_limit | Adding it would leave the integration storing more than 10,000 resources; the active namespaces were still applied. |
deadline | The scan reached the last 30 seconds of its two-minute deadline, kept for the upload, before this namespace. |
error | Any other failure, such as another HTTP status or an invalid list response. |
The access manifest holds only a Role and RoleBinding per configured namespace, bound to the collector's ServiceAccount in the home namespace. It has no Secret and no token, so applying it again never rotates the credential. A skipped namespace stays pending and is retried every cycle.
kubectl --context your-cluster apply -f probara-discovery-<integration-prefix>-access.yaml
# After removing a namespace, optionally delete the collector's Role there:
kubectl --context your-cluster -n <removed-namespace> delete role,rolebinding probara-discovery-<integration-prefix> --ignore-not-foundA removed namespace leaves coverage at once (summary, totals, search and paging), and approving or linking on its services is refused. The next scan marks its resources missing, and retention deletes them, with their services and monitor links, 7 days later. Monitors and their history are never touched. Adding it back within those 7 days restores the same services and links once it is scanned again. Edits are deltas, so concurrent editors and MCP agents merge: adding a configured namespace or removing an absent one does nothing. An edit never conflicts with a scan in flight; a scan that raced a removal gets 409 with Retry-After: 10, and the collector retries with its new configuration within seconds.
Understand the coverage table
Coverage and health are separate. The table reports missing enabled monitor links, stale check evidence, unreachable alert routing, missing resources, and stale inventory. Alert reachability uses the same shared rules as the alerter, including group roll-up. It reports configured reachability, not successful delivery to a provider. A healthy check can still have a routing gap; a failing check can be fresh and properly routed.
Check evidence is fresh by the same rule the platform uses for absence detection: received by Probara within three monitor intervals (at least 90 seconds), or interval plus grace period for push monitors, counted from the monitor's creation until its first result arrives, so a just-approved monitor starts fresh. The worker's own timestamps are not used. Group freshness cannot be assessed from check results; link leaf checks. Inventory becomes stale after 15 minutes without a complete scan and blocks new approvals; it is reported once for the whole integration and is left out of the summary counts and the gaps filter. Missing resources have a 15-minute grace period; the Missing count includes only services past it, and services still within it are noted beside it. Missing or stale inventory never deletes a monitor. Unlinking and deleting an integration preserve monitor history.
Reconciliation and limits
Every scan must list both resource kinds in every active namespace, including all pagination pages. Any list failure there aborts the upload; a pending namespace that fails is only skipped (see Change namespaces). The API validates the complete snapshot under a revision check and applies it transactionally; stale or replayed snapshots return 409. A valid empty snapshot marks resources missing, while an incomplete scan leaves the prior inventory intact.
Limits: 20 integrations per workspace, 20 namespaces per integration (active and pending together), 2,000 resources per scan, the first 32 suggestions per resource, and 4 MiB per page/upload. Each cycle has a two-minute deadline. The API processes at most one snapshot per minute per integration, counting snapshots refused for the resource limit (429 with Retry-After otherwise; the collector honors Retry-After on 409, 429 and 503), and stores at most 10,000 resources per integration, present and missing together; a snapshot that would exceed that with its active namespaces alone is refused with 422 inventory_limit and leaves the inventory unchanged, while pending namespaces that would exceed it stay pending as inventory_limit. Wait for retention to reclaim missing resources, remove namespaces (their resources count until pruned, 7 days after they go missing), or connect a replacement integration with fewer namespaces. Resources missing for more than 7 days are deleted, and so is any service left with no resources and no monitor links. A service left with no resources that still has monitor links stays, reported missing, until 7 days after it last had a present resource; it is then deleted with its links. Monitors and their history are never deleted. Only the home namespace is fixed; see Change namespaces.
Management APIs live under /api/v1/discovery and use tenant authentication and normal write permissions; MCP clients reach the same routes as tools (get_discovery_coverage, update_discovery_integration, approve_discovery_suggestion, …). The scoped collector token works only on /api/v1/discovery-collectors/{id}/config and /snapshot; it cannot create monitors. Snapshot data is untrusted: the API decides which targets are internal (every Service address, plus single-label, IP, .svc, .cluster.local, .local and .internal hosts) and so need a private location, whatever the collector reports, and refuses a Service suggestion that does not target that Service's own cluster address. HTTPS verification remains enabled and collector clients refuse redirects. The internal flag is a routing hint, not an SSRF control: it is decided from the name alone, so a public-looking hostname that resolves to a private address is classed external and may run on the shared fleet. Set `HTTP_BLOCK_PRIVATE_IPS=true` on fleets that run discovered checks, and allow a private location only the ranges it needs with HTTP_ALLOWED_CIDRS.