Skip to content
Console Pages

Console Pages

What each DevEx page in the console shows, the endpoint it reads, and the rules that endpoint keeps. Every /api/* GET here needs a console session. The handlers live in grpc/operators/core/internal/console/.

How to read absence. These endpoints answer 200 even when they could not read a source, and say so in the body (source:"unavailable", complete:false, or an unmeasured[] list with the API’s own reason). An empty list means “none”; an unreadable one is reported as unmeasured, never as empty.

Cluster

Namespaces — GET /api/namespaces

For every tracked namespace plus every tenant client-* namespace, it shows the admission posture (PSA labels, NetworkPolicies), the workloads (Deployments, DaemonSets, StatefulSets) with ready/desired, their pods (restarts, node, why the last container died), Warning events, and ResourceQuota/LimitRange. Each namespace closes with one verdict and the sentence that justifies it (cluster_namespaces.go). A “Nodes & placement” sub-tab covers nodes; GET /api/nodes/{name} inspects one node.

Container logs are not read, by design: they can carry secrets, and the operator’s ServiceAccount has no pods/log grant.

Instances — GET /api/dashboard

The tenant instances (ClientInstances) with app, tenant, role, phase, readiness and endpoint, plus the FinOps attribution projected from the CR (initiative, compute units, tier, expiry).

Routing — GET /api/routes

How traffic reaches each service, and whether the thing it reaches is serving (routing.go). The routing table is the DEVGW_ROUTES env var on the devgateway DaemonSet, plus the console’s own /forge/ path. The table’s origin is reported as:

OriginMeaning
podread from a running devgateway pod, so it is what routes right now
daemonsetread from the DaemonSet template, so it is what the next pod gets
declaredthe committed manifest, used only when the edge could not be read. No route with this origin is called “serving”

Platform audit

An assessment page, not the audit log. It gives one verdict over checks read from existing endpoints: /api/audit/verify, /api/gitops, /api/doctor, /api/metrics, /api/reviews, /api/compliance and /api/governance-runs. A check that could not be read goes into a not-measured list, never counted as a pass. Its only actions are the ones the console already has: re-verify the audit chain, and start a review through an existing playbook’s PlaybookService.RunNow.

GitOps — GET /api/gitops

The response has one of two modes:

  • mode: "argo": the Argo CD Application list could be read. It shows sync and health per Application.
  • mode: "github-actions": Argo CD is absent, which is the case on the real box, where delivery is core-cd.yml. Per workload (gitops_delivery.go):
    • desired is the newest core-cd.yml lineage record that planned or rolled the workload: commit, image, digest, run, time and roll verdict;
    • running is the pods’ imageID digests and the placement labels on the live object (app.kubernetes.io/version, runink.org/commit);
    • drift is decided on the digest when the build reported one, else on the commit label. It is null when neither pair was read, never false.

The commit stamp. core-cd.yml’s roll job makes one strategic-merge kubectl patch of the pod template. It sets the label runink.org/commit=<sha> and the annotation kubectl.kubernetes.io/restartedAt, and re-pins every container on the node-local registry to :latest@<digest>. If the patch cannot be built or is refused, it falls back to a plain rollout restart with a ::warning.

Because of the stamp, CD-rolled workloads are digest-pinned. A bare kubectl rollout restart restarts onto the same digest. To move a workload onto new bits, dispatch core-cd.yml with images=<img>.

“Did my commit ship?” is Deploy lineage (GET /api/lineage?sha=), on the Intelligence rail. See Data audit & deploy lineage.

Audit chain — GET /api/audit, GET /api/audit/{seq}, GET /api/audit/verify

The console’s hash-chained audit log, persisted in its appfs root at var/log/audit and continued across restarts (auditlog.go, auditread.go).

  • GET /api/audit returns a newest-first page and the chain’s verdict. Filters: actor, action, outcome (success, denied or failure), since (RFC 3339 or a duration such as 24h), limit, before (a chain seq).
  • GET /api/audit/{seq} returns one record in full with its neighbours’ hashes. Records carry seq, at, actor, action, target, outcome, detail, hash, prevHash.
  • GET /api/audit/verify walks the persisted chain and names the first record that was edited, removed, inserted or reordered. 200 with ok:false is a detected break; 503 means there is no persisted chain to check.

Only session admins may read the trail, and the check fails closed. It is cross-identity data: sign-ins, refused sign-ins, revocations, and who armed which agent.

Session admins — GET/PUT /api/session-admins

Who may read the audit trail and end another person’s sessions. The list is a document in this installation’s metastore (var/lib/session-admins), not a manifest value (sessionadmins.go):

  • CONSOLE_SESSION_ADMINS in core-console-secrets is the first-boot seed only. Once the document is written, changing the Secret changes nothing.
  • Unset means nobody, unlike the other admin lists.
  • PUT replaces the list. It refuses an empty list, and one that drops the caller unless someone else remains. Every attempt is audited, and a change is made durable in the audit log before it is written.
  • A console with no sign-in configured refuses: this console has no sign-in configured, so no identity can be a session admin.

Doctor — GET /api/doctor

Traces agents/opsdoctor, which is a k8s CronJob rather than a workflow so that it keeps working when the runner pool it watches is dead. It shows the CronJob’s state (present, SUSPENDED, schedule, last run times), its recent Jobs and how they ended, and the tracking issue (label ops-doctor) with the time of the last report. Suspension is surfaced first, because a benched doctor looks exactly like a healthy quiet one.

Metrics — GET /api/metrics

Measured process and workload metrics only (procmetrics.go). They come from this process (runtime and /proc) and the live API server. There is deliberately no uptime percentage and no network throughput, because the process cannot measure either. Every block carries a source.

Live CPU and memory come from metrics.k8s.io when it exists. When it is absent (as on the real box), they come from the kubelet Summary API through the apiserver node proxy (kubeletstats.go), labelled source: "kubelet-summary". That needs nodes/proxy get on the core-operator ClusterRole. A 403 is reported as unmeasured, never zero.

Agent runs

Runs — GET /api/runs

One history of every autonomous agent run, whatever started it (runs.go). It is a view over stores that already exist:

KindSource
workflow agentsthe run store, all repos it holds, topped up through /api/agent-runs’ staleness gate
pipelinesthe run store’s pipeline runs
CLI sessionsthe session-report table and your own command queue
CronJobsthe batch Jobs of opsdoctor, certsync, ddnssync and imagesync (no log: pods/log is not granted)
app agents/api/agent-health, latest only

The “Schedules & arming” tab reads GET /api/agent-schedules. See Agent Fleet.

Reviews — GET /api/reviews, GET /api/reviews/{type}/{runId}[/detail]

What each review found, per type (reviews.go):

TypeRuns fromFindings from
coderun store: reviewer, fixer, resolveropen PRs labelled review:findings-open
riskrun store: riskthe “🛡️ Dependency risk report” issue
compliancerun store: compliance/api/compliance plus the “📋 Compliance status” issue headline
dataQualityrun store: datagov, plus datagov assessments and CapEx scan runsthe datagov findings store and the CapEx findings
dataConsistencyCapEx scan runsthe CapEx “Consistency” control dimension
aiMaturity, dataMaturitynone, computed on read/api/maturity

A run whose findings were not recorded carries summary: null and findingsStored: false with the reason, never a zero count that would read as a clean review. /detail adds what the agents posted on GitHub.