Trust & access
The Trust sub-section of DataEx holds Harness, Guardrails & autonomy, Policy & ReBAC, Admin and Secrets & PKI. This page also covers the rule under all of them: how the console decides who may change something.
Sign-in
A session requires a configured sign-in method: GOOGLE_CLIENT_ID (Google) or CONSOLE_ADMIN_PASSWORD (password). Who may sign in is decided by emailAllowed in auth.go:
CONSOLE_ALLOWED_EMAILS: an address list. This is the console’s own variable, not the apps’AUTH_ALLOWED_EMAILS.CONSOLE_ALLOWED_HD: a hosted e-mail domain, consulted only whenCONSOLE_ALLOWED_EMAILSis unset.- With neither set, nobody may sign in (fails closed).
Sessions are server-side, in the console’s appfs root (run/sessions). /auth/logout revokes the current session. If the session store cannot be mounted (usually a missing CORE_ENVELOPE_KEK or an unreachable objectd), the console logs CONSOLE SIGN-IN DISABLED at boot and refuses every sign-in.
The privileged-write rule
A GET is session-gated (requireSession). A write that changes who is trusted, what runs or what is provisioned also passes one predicate, adminRefusal (auth.go), and is audited on success, failure and refusal:
- No sign-in configured: the write is refused outright (403), because nobody could be attributed.
- No verified identity on the request:
no verified console identity on this request. - The allowlist is set and does not name you:
<you> is not permitted to <what> on this console (<ENV>). - The allowlist is unset: every identity this console admits is allowed. That is not a wildcard, because sign-in itself already fails closed.
Allowlist (optional keys in core-console-secrets) | Governs | Unset means |
|---|---|---|
CONSOLE_AGENT_ADMINS | arming agents, acting on Harness findings, changing guardrails, and POST /api/github | every signed-in operator |
CORE_CONNECTION_ADMINS | the connection registry, POST /api/providers, connector probe refresh | every signed-in operator |
CONSOLE_INSTANCE_ADMINS | the /chat tools that mutate (create_client_instance) | every signed-in operator |
CORE_ATLAS_ADMINS | Atlas gRPC-web writes, including Resolve’s dialing actions and connection Test/Verify | every signed-in operator |
CORE_RUNNER_ADMINS | enrolling, re-addressing, connecting and revoking data-access runners, and their tokens | every signed-in operator. Exception: deploying a runner refuses everyone while unset |
CONSOLE_SESSION_ADMINS | ending another person’s sessions and reading the audit trail | nobody (everyone may still end their own) |
CONSOLE_SESSION_ADMINS is the odd one out: it only seeds this installation’s metastore on first boot. After that, the list is edited through GET/PUT /api/session-admins (DevEx › Session admins). CORE_PLATFORM_ADMIN names the installation’s platform admin, whom the operator makes admin on every instance.
Policy & ReBAC (?tab=policy)
GET /api/access (session, read-only) renders the table above live. For each list it reports whether the list is set and its size, and only your own membership. It also lists every ClientInstance with a count of admin, writer and reader grants and your own role, and whether CORE_PLATFORM_ADMIN is configured.
It always carries the entry rebac in unmeasured[]: relationship-based access (security/policy) “is available as a library and wired into nothing here”. No route, agent or app checks a ReBAC relation. Access today is the allowlists and per-instance personas.
Machine-ingest tokens
Agents and apps do not use sessions. Each machine door takes a shared secret generated once in core-system by the provisioner (provisioner/secrets.go), projected optional: true, and refusing everything while unset:
| Env | Door |
|---|---|
CORE_HEALTH_INGEST_TOKEN | app agents’ POST /api/agent-health |
CORE_RECON_INGEST_TOKEN | the recon agent’s POST /api/rules-recon/report and GET /api/rules-recon/inputs |
CORE_FORGE_IDENTITY_KEY | the X-Core-Identity key CORE uses to vouch for a person to FORGE |
CORE_ROUTER_IDENTITY_KEY | the model router’s interactive lane |
CORE_JUDGEMENT_INGEST_TOKEN | POST /api/judgement/submissions. No manifest provides it; see Judgements |
CORE’s own agents report with a GitHub App installation token for the console’s org instead (authorizeReporter). That covers /api/data-governance-findings, /api/data-governance-estate, /api/judgements, /api/session-runs and the playbook doors.
Harness (?tab=harness)
GET /api/harness joins /api/measures (the platform’s self-assessment) and /api/compliance (per-control findings) into one list of findings. Each finding offers only actions CORE can really take:
| Action class | Executor |
|---|---|
dispatch_agent | repository_dispatch to a named agent workflow, with the console’s App installation token |
file_issue | a tracking issue in the agents repo carrying the evidence |
needs_human | an acknowledgement, recorded |
An agent that addresses a domain but that the console cannot start is shown as an unavailable action with the reason.
POST /api/harness/{id}/act runs one, in this order:
- Session plus
CONSOLE_AGENT_ADMINS. - The finding must be live now.
- The guardrail level must not be
off. - An idempotency reservation: a repeat within the window replays the earlier result.
- Audit before act: if the intent record is not durable in the chain, nothing happens and the answer is 503.
- Act, then record the outcome.
Guardrails & autonomy (?tab=guardrails)
GET /api/guardrails (session) and PUT (admin, CONSOLE_AGENT_ADMINS). The page sets an autonomy level per action class, optionally per domain: off (refused), hitl (a human clicks) or auto. Everything defaults to hitl. Levels are stored in the console’s harness-guardrails table and audited before the store is written. Hard guardrails, drawn from rules the platform already enforces, cap a finding at hitl or refuse it, whatever the level.
auto is recorded and never executed in this build. There is no unattended executor. Every act is a signed-in admin’s POST, and the payload says so on every response (autoExecution).The same payload carries modelGuardrails: the model-call guardrails, read from the console’s one security/guardrails Guard (profile console-ask). The console refuses to start if the Guard cannot be built. /chat and Ask screen what a person typed before any model call, and screen the model’s text before it is shown or acted on.
Admin (?tab=admin)
The members and seats of the instance chosen on the landing page, over the shared org-runink/ui AccessService (access_ui.go). The rail draws it only when the server says you are an admin of that instance, and every access RPC re-checks the role. An admin is a spec.users entry with role admin on that instance’s live ClientInstance (expiry honoured), or CORE_PLATFORM_ADMIN. It is never one of the console capability lists above: those administer the console, not an instance’s membership.
Changes are a JSON merge patch of the whole spec.users array, carrying the CR’s resourceVersion. A raced patch is therefore refused rather than silently undone. A change is reported PENDING until the operator re-projects the app’s users and the app rolls. Seats go through the billing seat RPCs in-process. The ui component’s rules: nobody changes their own access, the last admin stays, and a full seat pool refuses an invite.
Secrets & PKI (?tab=secrets)
GET /api/security reports the shared mesh CA (the core-mesh-ca Secret, key MESH_CA_CERT) as distributed to each platform namespace: subject, SHA-256 fingerprint, notBefore/notAfter and days remaining. Use it to confirm that the root is identical across namespaces and to catch an expiring CA. The mesh leaves rotate in-process and are never stored, so the CA is the artifact to inspect. The page degrades to source:"unavailable" when the CA has not been distributed anywhere reachable. The “Model guardrails” rail on this endpoint is read from the Guard, never from env.