Connection
A Connection (short name conn) names an external service a sandbox may
reach: the hosts it lives on, and a credential the customer created and
scoped. A sandbox gets a Connection by binding it to a role its
class
declares.
Omnia does not interpret a Connection or a role. Provisioning is the same for
every service: place the credential as placement says, and add hosts to
the allowlist of the sandbox’s egress proxy. Omnia mints no
credentials of its own. A gitHost Connection’s are
each user’s, linked by that user and minted per call from what they linked.
API version
Section titled “API version”apiVersion: omnia.altairalabs.ai/v1alpha1kind: ConnectionSpec fields
Section titled “Spec fields”apiVersion: omnia.altairalabs.ai/v1alpha1kind: Connectionmetadata: name: github-acme namespace: agentsspec: hosts: [github.com, api.github.com] credential: kind: secret secretRef: name: acme-github-token placement: env: - name: GH_TOKEN key: token files: - path: .git-credentials key: git-credentials| Field | Type | Required | Description |
|---|---|---|---|
hosts | []string | Yes | Hostnames a sandbox bound to this Connection may reach, 1 to 32. Lowercase, at least two labels. A leading *. matches any subdomain but not the apex |
credential.kind | string | Yes | none, secret, browserSession or gitHost. See Credential kinds |
credential.secretRef | {name} | For secret | A Secret, by name, in the Connection’s own namespace. Refused for any other kind |
credential.browserSession.scope | string | No | perUser (the default): each end user signs in as themselves. shared: one session every user browses as. Immutable |
credential.browserSession.signedOutWhen.urlPrefixes | []string | No | The site’s signed-out URLs, normally its sign-in page, 1 to 16. Each is https://, a host and an optional path, with no port, query or fragment. The host must be one of hosts |
credential.browserSession.signInURL | string | No | The page a person starts on when signing in through an enrolment, in the same form. Its host must be one of hosts: the operator refuses an enrolment otherwise, and the admission webhook warns. Absent, the first signedOutWhen.urlPrefixes entry is used; a Connection with neither cannot be enrolled |
credential.browserSession.encryption | {kmsProvider, keyID, secretRef} | For browserSession | The KMS key captured sessions are sealed with. kmsProvider is aws-kms, azure-keyvault, gcp-kms or vault, or local, which is not a KMS: a key in a Secret, for development only, never production. kmsProvider and keyID are immutable; secretRef names the provider’s credentials, in the keys a SessionPrivacyPolicy’s encryption secret uses |
credential.gitHost.provider | string | For gitHost | github or gitlab, whose users link their account through the OAuth app in oauth, or token, whose users each paste a personal access token for any https host. Immutable |
credential.gitHost.oauth.clientID | string | For github, gitlab | The OAuth app’s client ID. The whole oauth block is required for github and gitlab and refused for token |
credential.gitHost.oauth.clientSecretRef | {name, key} | For github, gitlab | The Secret, in the Connection’s namespace, holding the OAuth app’s client secret. key defaults to clientSecret. Only privacy-api reads it |
credential.gitHost.oauth.baseURL | string | No | The host’s web root for GitHub Enterprise Server or a self-managed GitLab, such as https://github.example.com. https://, a host, an optional port and path. Absent means github.com or gitlab.com |
credential.gitHost.encryption | {kmsProvider, keyID, secretRef} | For gitHost | The KMS key linked credentials are sealed with, in the same form as browserSession.encryption. kmsProvider and keyID are immutable |
placement.env | []{name, key} | No | Environment variables set from the Secret’s keys, at most 32 |
placement.files | []{path, key | template} | No | Files written from the Secret, at most 16. path is relative to the sandbox user’s home directory and may not contain ... Set exactly one of key (the file is that key’s value) or template (a Go text/template whose data is the Secret’s keys) |
placement is accepted only for a secret credential, browserSession
exactly for a browserSession one, and gitHost exactly for a gitHost one.
A browserSession or gitHost credential cannot be changed to another kind;
delete the Connection instead.
Credential kinds
Section titled “Credential kinds”| Kind | What it does | Accepted by |
|---|---|---|
none | Contributes hosts to egress and nothing else. This is how a sandbox reaches a public site | any role |
secret | A customer-created Secret, placed as placement says | any role; files need the class to set HOME |
browserSession | A captured browser session. See Browser sessions | a role that declares session delivery |
gitHost | Each user’s own credential for a git host, minted for the operator per call. See Git host credentials | any role, which gets its hosts and no credential |
Binding a Connection to a sandbox
Section titled “Binding a Connection to a sandbox”A role is an opaque name, such as vcs or demo_sharepoint_account. Three
objects take part:
- The SandboxClass declares the roles it offers in
spec.connections, each optionallyrequired. - The ToolRegistry sandbox binds a default Connection to a role in
its
connections. - The AgentRuntime overrides a role for one agent in
spec.sandbox.connections, keyed by sandbox name.
# SandboxClass: the rolesspec: connections: - role: vcs required: true - role: demo_sharepoint_account
# ToolRegistry sandbox: the defaultsspec: sandboxes: - name: workspace class: {name: codegen-default, kind: ClusterSandboxClass} connections: - {role: vcs, connection: github-shared}
# AgentRuntime: this agent's overridesspec: sandbox: enabled: true connections: - {sandbox: workspace, role: vcs, connection: github-acme} - {sandbox: workspace, role: demo_sharepoint_account, connection: demo-sharepoint}For each role the agent’s binding wins, then the sandbox’s default; a role
bound by neither is unbound. When the operator creates a
SandboxClaim it copies the result
onto spec.connections. Only the operator may write a claim with bindings (an
admission policy refuses anyone else), and the agent never chooses a
Connection.
Placement
Section titled “Placement”When a claim binds, the operator puts each bound secret Connection’s
placement into the codegen sandbox container:
- Environment variables reference the Connection’s Secret
(
valueFrom.secretKeyRef). The pod spec never holds a value, and a rotated Secret reaches the sandbox when its pod restarts. - Files are rendered by the operator into a Secret named
<claim>-sandbox-connections, owned by the claim, and mounted read-only (mode0440) one file at a time under theHOMEthe sandbox’s class sets. A class that sets noHOMErefuses placed files (ConnectionFilesUnsupported). Atemplateruns with every key of the Secret as data and refuses a key the Secret does not have. The rendered Secret is rewritten on each reconcile of the claim.
The CRD refuses an env name the operator sets itself (HOME, and
HTTPS_PROXY, HTTP_PROXY, NO_PROXY in either case), and a file path with an
empty, . or .. segment. A claim also refuses any variable its class sets
(ConnectionEnvReserved), and a file at or under a directory of HOME that
the class’s own variables point into, such as a toolchain cache
(ConnectionPathReserved).
A claim that has not bound fails before its pod is built when the placement cannot be built, with one of the reasons under Checks.
A Bound claim is placed without the bindings whose Connection, Secret or key
has gone, so deleting any of them revokes the credential: the sandbox pod
restarts without it, the claim’s rendered files are deleted, and its egress
proxy is rebuilt without the Connection’s hosts. The operator watches both
Connections and the Secrets they name, so this happens when the object
changes.
Browser sessions
Section titled “Browser sessions”A browserSession Connection signs a browser sandbox in to a site. What it
holds is the session a person’s browser has after signing in: cookies and
storage, never a password or a one-time code.
spec: hosts: [portal.example.com, sso.example.com] credential: kind: browserSession browserSession: scope: perUser signedOutWhen: urlPrefixes: [https://portal.example.com/login] encryption: kmsProvider: aws-kms keyID: alias/omnia-browser-sessions secretRef: name: kms-credentials- Nothing is placed. The sandbox loads the session itself, so it never appears in the pod spec, a Secret or the claim.
- A sandbox signs in as one Connection. Binding two
browserSessionConnections to one sandbox failsConnectionSessionConflict. - The sandbox says when it needs a person. Until a session has been
captured, every browser tool call answers
connection_not_enrolled. Once the browser reaches one ofsignedOutWhen.urlPrefixes, every call answersconnection_reauth_required. The sandbox never signs in on its own. sharedis one account for everyone. Every user of every agent bound to the Connection browses as the account that signed in, and can see what the others did there. The operator’s admission webhook warns when a Connection is created or changed withscope: shared.
Where a session is kept
Section titled “Where a session is kept”The workspace’s privacy-api stores captured sessions, one per user of a
perUser Connection and one in all for a shared one.
- Only the Connection’s hosts are kept. A captured session loses every
cookie and every origin’s storage that does not belong to one of
hostsbefore it is stored. A domain cookie set for a parent domain of a host, such as.example.comforportal.example.comor for*.app.example.com, is kept, because the browser sends it to that host. A host-only cookie is kept only when its own host is one ofhosts. - Sealed with
encryption. privacy-api stores a session only after sealing it under the Connection’s KMS key, and records which key sealed it. It opens a stored session for one caller only: the browser sandbox it is bound to, as the next section describes. - Only the dashboard stores or deletes one. privacy-api accepts those calls from the dashboard’s ServiceAccount alone, never from an agent’s pod.
- Erased with the user and with the Connection. A deletion request for a user erases that user’s sessions. Deleting the Connection, or re-creating one under the same name, erases every session captured under it within the next sweep, every ten minutes by default.
- Audited. privacy-api’s audit log records each session stored, each session delivered to a sandbox and each session deleted, with the user and the Connection, and never a cookie or a stored value.
| Route on privacy-api | Does |
|---|---|
PUT /api/v1/browser-sessions/{connection}?virtualUserId= | Stores a Playwright storage state, at most 1 MiB |
GET /api/v1/browser-sessions/{connection}/status?virtualUserId= | Says whether a session is stored, and when |
DELETE /api/v1/browser-sessions/{connection}?virtualUserId= | Deletes one user’s session, or every user’s with all=true in place of virtualUserId |
GET /api/v1/browser-sessions/{connection}/state | Returns the session to the sandbox bound to it, which presents its operator-signed token |
A shared Connection takes neither virtualUserId nor all. A perUser one
needs virtualUserId, except that a delete takes all=true in its place.
Signing in from an enrolment sandbox
Section titled “Signing in from an enrolment sandbox”An enrolment lets a person sign in to the site itself, in a browser sandbox the operator runs for them. The person drives the sandbox’s browser through a live view; when they finish, the sandbox stores the session in privacy-api and ends. Neither the dashboard nor the agent ever holds the cookies.
- Who may enrol. On a
perUserConnection, any workspace member, only for themselves. On asharedone, workspace owners only. An agent never can: the operator refuses a delegated identity outright. - What runs. A
SandboxClaimwithspec.enrolment, which only the operator creates, on the install’sbrowser-enrolClusterSandboxClass. Its pod reaches the Connection’shoststhrough the claim’s egress proxy, and privacy-api, and nothing else; only the dashboard’s pods (for the view) and management-api’s (for Done and reload) reach its view port. - Where it starts.
signInURL, or the firstsignedOutWhen.urlPrefixesentry. - What is kept. Only cookies and origins under the registrable domain of
each of
hosts(forlogin.example.com, everything underexample.com), so the identity provider’s and every tracker’s cookies are dropped. - How it is stored. With a single-use write grant the operator mints for that claim, Connection and user. privacy-api checks the claim exists, was written by the operator and enrols this Connection for this user.
- How long. The sandbox has five minutes to start, then the person has
maxDuration, 15 minutes by default (1 to 20). An enrolment not finished by then ends unsaved.
The operator’s REST endpoint is
POST /api/v1/workspaces/{workspace}/connections/{connection}/enrolments (see
api/CHANGELOG.md). People enrol from the Connection’s row in the dashboard’s
Connections list: see
Sign in to a site for an agent.
The dashboard
Section titled “The dashboard”The Connections page lists each Connection with one status and one action; there is no page per Connection. Add connection offers four kinds, and fills in the rest of the spec:
| Kind | The person gives | The dashboard writes |
|---|---|---|
| Website | a name and an address | hosts from the address, credential.kind: none |
| API token | a name, an address, the token and an environment variable | a Secret connection-<name>-token (key token) owned by the Connection, credential.secretRef naming it and placement.env |
| Sign in with a browser | a name, the sign-in page and who signs in | signInURL and signedOutWhen.urlPrefixes from the page, scope, and encryption from the install |
| Git host (Enterprise) | a name, the host’s address, the provider and, for GitHub or GitLab, the OAuth app’s client ID, the Secret holding its client secret and an optional server address | hosts from the address, gitHost with provider and oauth, and encryption from the install. See Git host credentials |
- The token is write-only. It is sent once, with the create or a Replace token, and written into the Connection’s own Secret by the dashboard’s server. No dashboard route returns it: the list says only whether one is set, and when. Deleting the Connection deletes that Secret (Kubernetes garbage-collects it through its owner reference) and no other. Under Advanced, a Connection can name a Secret you manage instead.
- Encryption is set once for the install, in the chart value
connections.browserSession.encryption, and the dashboard’s server writes it into each browser Connection it creates. With no key set, or no browser-enrol class, the browser choice is off and says why. - The dashboard keeps the name you typed and the address in the annotations
omnia.altairalabs.ai/display-nameandomnia.altairalabs.ai/address.
The dashboard’s routes for a person’s own session forward to privacy-api with the dashboard’s ServiceAccount token:
| Dashboard route | Role | Forwards to |
|---|---|---|
GET /api/workspaces/{name}/connections/{connection}/browser-session/mine | viewer | GET …/{connection}/status |
DELETE /api/workspaces/{name}/connections/{connection}/browser-session/mine | viewer; owner for shared | DELETE …/{connection} |
They take no subject: the subject is the signed-in user, so any member can
see whether they are signed in and sign themselves out. On a shared
Connection they answer for the one shared session, which any member may see
and only an owner may sign out. privacy-api refusing the dashboard’s own
token (401 or 403) is reported as a 502. A workspace with no privacy-api
answers 501, or 503 while a requested one has no resolved URL.
The dashboard does not import storage-state files. privacy-api’s PUT route
above still accepts one from the dashboard’s ServiceAccount.
How a session reaches the sandbox
Section titled “How a session reaches the sandbox”The session stays sealed in privacy-api until the sandbox pod starts. Its
init container, session-fetch, asks privacy-api for it, writes it to the
pod’s memory-backed /tmp, and exits; the browser bridge loads it and
deletes the file before the first tool call.
- The token is claim-scoped and short-lived. The operator signs a token
for each claim that binds a
browserSessionConnection and keeps it in a Secret the claim owns,<claim>-sandbox-session. It names that claim, its conversation, the one Connection, the workspace and that workspace’s privacy-api, and lives as long as the runtime’s sandbox idle timeout, at least five minutes and at most an hour. The operator mints a new one when half of that has passed, so a pod that restarts later still starts with a valid one, and deletes it once the claim binds no session. - Only the init container holds it. The browser container never mounts the token, so nothing the agent drives in the browser can read it.
- Only the operator’s own claims get one. A claim written by anyone other than the operator gets no token, and privacy-api refuses a token whose claim is not the operator’s, serves another conversation, or does not bind the Connection.
- A
perUsersession is the conversation’s user’s. privacy-api reads the user from the conversation the claim serves, in session-api. Nothing the sandbox sends can name another user. - The sandbox reaches privacy-api and nothing more of the cluster. The
sandbox’s network policy opens privacy-api’s pods for a claim with a session
to fetch, and only for that claim. The browser cannot use that path: every
request it makes, to
localhostincluded, goes through the claim’s egress proxy, which dials public addresses only. - Not enrolled is not a failure. With nothing stored, the pod starts and
every browser tool call answers
connection_not_enrolled. A refusal is asked again for up to 90 seconds, since the conversation’s user may not be recorded yet, and then the pod starts the same way, without a session. Only privacy-api failing, or being unreachable, holds the pod in its init container. - The session is fetched once per pod. The browser container restarting
on its own does not fetch it again, and its tool calls answer
connection_not_enrolleduntil the pod is recreated.
Git host credentials
Section titled “Git host credentials”A gitHost Connection holds no credential itself. Each user links their own
account with the git host, or adds their own personal access token, and the
operator uses that user’s credential when it clones, pulls or pushes on their
behalf. There is no shared form.
spec: hosts: [github.com] credential: kind: gitHost gitHost: provider: github oauth: clientID: Iv1.0123456789abcdef clientSecretRef: name: github-oauth-app encryption: kmsProvider: aws-kms keyID: alias/omnia-connectionshostsare the remotes it covers. The operator picks a Connection by matching a repository remote’s host against them, as the egress proxy matches them. Remotes arehttpsonly.- An OAuth app for GitHub or GitLab. The app’s callback URL is the
dashboard’s base URL (
OMNIA_BASE_URL) followed by/api/git-credentials/callback, one URL for every workspace and Connection. The client secret is read from a Secret in the Connection’s namespace, underclientSecretor the keyclientSecretRef.keynames. GitHub asks for thereposcope; GitLab forread_userandwrite_repository. Work on an external repository describes the setup. - A token for any other host. With
provider: token, each user pastes a personal access token, with an optional username for hosts that need one over https. It is stored sealed and never shown again. - External Arena projects use it. When you clone, pull or push an
external ArenaProject, the
operator mints your token for the host of the project’s
urlthrough thegitHostConnection in the project’s namespace whosehostscover it (one that names the host exactly wins over a wildcard). It uses the token for that one request and never stores it. With no such Connection, or none linked for you, the operator answers412“link your<host>account”. - Bound to a sandbox, it gives hosts only. A sandbox bound to a
gitHostConnection reaches itshoststhrough the egress proxy and gets no credential. - A git source cannot use one. A
connectionRefon a git source naming agitHostConnection is refused: a source is synced for everyone, not for one user.
Where a credential is kept
Section titled “Where a credential is kept”The workspace’s privacy-api stores each user’s credential, sealed under the
Connection’s encryption key, with the account name on the host where it is
known and when the credential was last used.
- Linking an account runs the host’s authorization-code flow with PKCE. The dashboard starts it for the signed-in user; privacy-api keeps a single-use state for ten minutes, exchanges the code with the client secret, which only it reads, and stores the tokens the host issues.
- Only the dashboard links, reports or unlinks, and only the operator mints. privacy-api accepts each from its own ServiceAccount alone, never from an agent’s pod.
- A mint refreshes when it must. An access token within five minutes of
expiry is refreshed first, and a rotated refresh token is stored in the
same write. When the host refuses the refresh token, privacy-api deletes the
credential and answers
relink: the user links again. A personal access token is returned as it is. - The host must be the Connection’s. A mint for a host outside
hostsis refused. - Unlinking revokes where the host can. GitHub withdraws the app’s grant and GitLab revokes the token. A personal access token is deleted, and its owner revokes it at the host.
- Erased with the user and with the Connection. A deletion request for a user erases their credentials and any link in progress. Deleting the Connection, or re-creating one under the same name, erases every credential linked under it within the next sweep, every ten minutes by default.
- Audited. privacy-api’s audit log records each link, each deletion and each mint, with the Connection, the host, the user and the outcome, and never a token.
| Route on privacy-api | Caller | Does |
|---|---|---|
GET /api/v1/git-credentials/{connection}/mine?virtualUserId= | dashboard | Says whether the user is linked, as whom and when it was last used |
DELETE /api/v1/git-credentials/{connection}/mine?virtualUserId= | dashboard | Unlinks, revoking with the host where it can |
PUT /api/v1/git-credentials/{connection}/token?virtualUserId= | dashboard | Stores a personal access token, {token, username?} |
POST /api/v1/git-credentials/{connection}/oauth/start?virtualUserId= | dashboard | Starts an account link, {redirectURI} → {authorizeURL, state, expiresAt} |
POST /api/v1/git-credentials/{connection}/oauth/callback?virtualUserId= | dashboard | Completes it, {state, code} |
POST /api/v1/git-credentials/mint | operator | {connection, host, subject} → {username, token, expiresAt?} |
A refused mint says why in code: not_linked (404), relink (412) or
host_not_covered (403).
In the dashboard
Section titled “In the dashboard”Each git host row shows your own link state, “Linked as octocat · used 2 hours ago” or “Not linked”, and one action: Link your GitHub account, Link your GitLab account or Add a token, and Unlink once linked. Every member links their own; nobody sees or removes another’s. The dashboard’s routes take no subject, since the subject is the signed-in user:
| Dashboard route | Role | Forwards to |
|---|---|---|
GET, DELETE /api/workspaces/{name}/connections/{connection}/git-credential/mine | viewer | …/{connection}/mine |
PUT /api/workspaces/{name}/connections/{connection}/git-credential/token | viewer | …/{connection}/token |
POST /api/workspaces/{name}/connections/{connection}/git-credential/oauth | viewer | …/{connection}/oauth/start, then sets a ten-minute HttpOnly cookie binding the link to the browser |
GET /api/git-credentials/callback | viewer of the linked workspace | …/{connection}/oauth/callback, then back to Connections |
Egress
Section titled “Egress”A sandbox whose bound Connections name any hosts gets its own egress proxy:
a Deployment, Service and NetworkPolicy named <claim>-egress, owned by the
claim. The operator runs it from its own image with the claim’s hosts as the
allowlist, and points the sandbox at it with HTTPS_PROXY, HTTP_PROXY and
their lowercase forms, by the Service’s cluster IP. NO_PROXY exempts
localhost, fully-qualified in-cluster names, and every Service the class’s
egress.allowedServices names, by name.namespace, cluster IP and, for a
Service in the claim’s namespace, short name. The class’s egress governs
those.
The proxy tunnels HTTPS with CONNECT and does nothing else:
- It does not terminate TLS, so it never sees a credential or a request.
- It refuses plain HTTP, a port other than 443, and a host that is not a bound
Connection’s. A leading
*.inhostsmatches any subdomain, not the apex. - It resolves the host and dials only a public address. A host that resolves only to private, loopback, link-local or shared ranges is refused, and the address it dials is the one it checked.
- Each refusal carries an
X-Omnia-Egress-Refusedheader naming the reason, so a failing command can be explained from inside the sandbox.
The proxy pod has no service-account token. Its NetworkPolicy admits only its own claim’s sandbox, and lets it reach cluster DNS and public addresses on port 443. The workspace’s isolation policy does not select it. Editing or deleting a bound Connection updates the proxy’s allowlist, and the operator deletes the proxy when none of the claim’s Connections names a host.
Egress through the proxy is HTTPS only. Git over SSH, and any protocol a tool cannot send through an HTTP proxy, is not reachable.
Test-only: in-cluster fixture hosts
Section titled “Test-only: in-cluster fixture hosts”The chart’s sandbox.egressProxy.insecureTestHosts names in-cluster fixture
hosts, each with the Service that serves it on port 443:
sandbox: egressProxy: acknowledgeInsecureTestOnly: true insecureTestHosts: - host: login.fixture.test namespace: omnia-e2e service: login-fixture insecureTestSPKI: <base64 SHA-256 of the fixture certificate's public key>The switch lifts the private-address check for those names and nothing else:
- A fixture host applies to a claim only when one of its bound Connections already names it. The switch never adds a host to the allowlist.
- The proxy never resolves a fixture host. The operator reads the Service and
pins the host to its cluster IP (
-insecure-test-hosts=login.fixture.test=10.96.0.20). The proxy still takesCONNECTto port 443 only, and never accepts a loopback or link-local pin. - The proxy’s NetworkPolicy opens the named Service’s pods, on the target port behind its port 443, and no wider range. The cluster IP needs no rule of its own: the CNI sees each packet after kube-proxy has rewritten it to a pod.
- Until the Service exists, with a cluster IP, a selector and port 443, the claim’s proxy does not get the host. The condition says which host is missing, and the operator looks again every 10 seconds.
- With
insecureTestSPKI, the browser trusts exactly that certificate (--ignore-certificate-errors-spki-list), but only in a sandbox whose Connections name nothing but fixture hosts. Chromium trusts the key for every host, not one, so a claim that also reaches a real host keeps the proxy switch without the certificate trust, and its condition says why. The operator setsOMNIA_INSECURE_TEST_FIXTURESandOMNIA_INSECURE_TEST_SPKIon the sandbox for it; a class or a Connection cannot set either.
It announces itself everywhere it acts. The chart fails to render without
acknowledgeInsecureTestOnly: true, or with a host that is not a DNS name, and
prints a warning in its notes. The operator (--sandbox-egress-insecure-test-hosts)
and each affected proxy (-insecure-test-hosts) refuse to start without their
acknowledgement flag, and log at error level. Each affected claim gets a
Warning Event and the condition InsecureEgressTestHosts=True, whose reason
is InsecureEgressTestHostsEnabled, InsecureEgressTestFixtureUnresolved or
InsecureEgressTestSPKIWithheld.
Git sources
Section titled “Git sources”A git source (SkillSource, PromptPackSource, ArenaProject, ArenaTemplateSource)
can fetch with a Connection instead of a Secret:
spec: type: git git: url: https://github.com/acme/skills connectionRef: name: github-acme- The repository’s host must be one of the Connection’s
hosts, matched as the egress proxy matches them, and the URL must behttpson port 443 orsshon port 22. Anything else is refused, so a Connection’s credential never goes to a host it was not scoped to, or over plain HTTP. - A
secretConnection’s Secret is read with the keyssecretRefuses:usernameandpasswordfor HTTPS,identityandknown_hostsfor SSH.placementdoes not apply. - A
noneConnection fetches anonymously. AbrowserSessionorgitHostConnection is refused. secretRefandconnectionRefcannot both be set.
The controller that syncs the source fetches the repository itself, so the egress proxy is not involved.
Checks
Section titled “Checks”The same checks run in three places, so they cannot disagree:
- the ToolRegistry’s
SandboxConnectionsValidcondition, for the sandboxes’ defaults; - the AgentRuntime’s
SandboxConnectionsValidcondition, for what each of its sandboxes will get; - the SandboxClaim controller, before the claim binds.
| Reason | Meaning |
|---|---|
ConnectionRoleUndeclared | A binding names a role the class does not declare |
ConnectionRoleUnbound | A required role has no Connection. Not reported on a ToolRegistry, since an AgentRuntime may still bind it |
ConnectionNotFound | The bound Connection does not exist in the namespace |
ConnectionKindNotAccepted | A browserSession Connection is bound to a role that declares no session delivery |
ConnectionPlacementCollision | Two bound Connections set the same environment variable, or place files where one path is the other or a directory above it |
ConnectionSecretNotFound | A secret Connection’s Secret does not exist |
ConnectionSecretKeyMissing | An env or file key is not in the Secret |
ConnectionTemplateInvalid | A file template does not parse, or names a key the Secret does not have |
ConnectionSessionConflict | Two bound Connections are browserSession; a browser sandbox signs in as one |
ConnectionSignedOutHostUnreachable | A signedOutWhen URL is on a host the Connection does not name, so the egress proxy would never let the browser reach it |
ConnectionEnvReserved | A secret Connection’s placement sets an environment variable the operator or the sandbox’s class sets |
ConnectionPathReserved | A placed file lies at or under a directory of HOME that the class’s own variables point into |
ConnectionFilesUnsupported | A Connection places files and the class sets no HOME |
ConnectionCheckFailed | The API server could not answer a read the check needs. Reported on the ToolRegistry and AgentRuntime conditions only |
The checks build the placement from the Secret as it stands, so a condition
that reads True means a claim made now will place.
The AgentRuntime condition adds ConnectionSandboxUnknown, for a binding that
names a sandbox its ToolRegistry does not declare. A claim adds
ConnectionsNotGranted, for bindings the operator did not write.
See also
Section titled “See also”- SandboxClaim — the claim that carries the bindings, and the classes that declare roles
- Sandbox security — what a sandbox can and cannot reach