ArtzAIn

Security review · NVIDIA OpenShell

OpenShell connect, reviewed from the code.

ArtzAIn, the model-agnostic agent control plane, checks every governed change your OpenShell gateway makes before it commits, and signs each decision into a ledger nobody can quietly edit. This page tells your security team what ArtzAIn does on your host, what leaves it and where it goes, how the credentials work, and what happens when something fails or is attacked. We wrote every answer from the shipped code.

Release covered
artzain 0.6.42: the sidecar, the connect command and the connect script
Sidecar image
sha256:93a8d5dc…63193
OpenShell approved by default
0.1.2
As of
2026-10-09

01 · At a glance

ArtzAIn decides and records. OpenShell enforces.

Your OpenShell gateway calls a small ArtzAIn sidecar, running beside it on your host, before it commits a governed write. The sidecar asks the hosted ArtzAIn engine for a decision. ArtzAIn evaluates your policy bundle and seals the decision as a signed, hash-chained receipt, so you can prove later what was allowed and why. The sidecar then tells your gateway to commit the write or refuse it.

PromiseStatusAs built
ArtzAIn never reaches into your networkHoldsThe sidecar only calls out, over HTTPS, to one ArtzAIn origin. Nothing calls in. It listens on a Unix socket for your gateway and on a loopback port for its own command-line tool, both on your host.
ArtzAIn never sees your prompts or secretsHolds, with limitsIt sees no prompts. The sidecar drops every key named like a secret before it builds a decision. A secret you place in a field with another name, such as a setting value, a policy or a draft's reason, is sent. Section 04 lists every field that goes.
No governed change commits without a decisionHolds, with limitsTrue for the writes the registration binds, on a gateway started with that registration, and every failure refuses the write. A gateway restarted without the registration is not governed: its heartbeat reports the registration missing, and ArtzAIn raises a finding for you. While ArtzAIn cannot answer, your host's user can open a break-glass window: the writes it lets through are journaled first, then receipted and reviewed (section 06).

The sidecar, the connect command and the connect script are open source under the Apache License 2.0. You can read them at github.com/CogNEXUSlabs/cognexus-tools, under python/src/artzain/openshell/.

02 · Scope

What ArtzAIn does, and does not

ArtzAIn governs the changes your gateway makes to what your sandboxes may do. It governs the action, never the model.

Governed writes

  • creating a sandbox, or deleting one;
  • changing a sandbox's policy, or a setting;
  • approving, rejecting, editing, undoing or clearing a policy draft;
  • creating, changing or deleting a provider or its profiles, and rotating its credential;
  • attaching a provider to a sandbox, or detaching it;
  • exposing a service, or removing one;
  • opening or revoking an SSH session.

What ArtzAIn does not do

  • Sandboxing, Landlock, seccomp and credential injection stay in OpenShell.
  • It does not see prompts, model input or output, or your sandboxes' traffic.
  • It does not receive provider credentials, environment variables or file contents.
  • It claims no proof for MCP, GraphQL, WebSocket, SQL or JSON-RPC traffic. Its policy prover covers filesystem, process, L4 and REST rules, and refuses to promote a bundle that relies on another protocol.
  • It does not run your OpenShell. You run your own gateway, from NVIDIA's deb or rpm package, on Linux x86_64 or aarch64.

03 · Trust boundaries

Two processes on your host, one connection out

Your gateway and the ArtzAIn sidecar run side by side as systemd user services. The only connection that leaves your host is the sidecar's outbound HTTPS to ArtzAIn.

BoundaryWhat crosses itControl
Gateway to sidecarEach governed write, before it commitsA Unix socket, 0600 in a 0700 folder, so only your gateway's user can open it. When your gateway signs its extension calls (gateway_jwt), the sidecar checks each call's Ed25519 token. Without that key, the host user is the boundary.
Local processes to sidecarThe sidecar's loopback HTTP routesA sidecar holding a gateway credential answers nothing but /healthz unless the caller presents the sidecar's own random token, compared in constant time. The connect command writes that token.
Sidecar to ArtzAInDecisions, reports, heartbeats, your base policyOutbound HTTPS to one origin. The client refuses any other origin, follows no redirect, and reads at most 256 KiB of an answer. Only the gateway credential authenticates it.
Your team to ArtzAInConnect requests, approvals, revokesA signed-in dashboard session. An approval is itself a sealed decision, under separation of duties (section 05).

04 · Data flows

What leaves your host, and what we keep

Every flow that crosses a boundary, when it happens, and what ArtzAIn keeps from it.

FlowWhenWhat is sentWhat ArtzAIn keeps
You ask for a connectOnce per gatewayA display name and the configuration: OpenShell release, interceptor and decision timeouts, telemetry choiceThe request; the gateway's id, which ArtzAIn assigns (gw_<ulid>); a sealed decision
An owner or admin approvesOnceThe approval. An account on its own adds a written attestationA sealed decision
ArtzAIn shows you the commandOn "Issue the command"The enroll token, shown once and marked not to be cachedThe token's peppered SHA-256 only
Your host installsWhen the command runsDownloads only, from GitHub and PyPI: the connect script, uv, and the wheels the script names by SHA-256Nothing
Your host redeems the tokenOnceThe enroll token, and the digest of the configuration you approvedWhen it was used, and from which address. You see both on your dashboard. Deleting your account clears the address
The sidecar asks for a decisionEach governed writeYour gateway as the agent (openshell:<gateway>), the action and target, and a payload: the method and the write's facts (sandbox id and name, the policy or the setting key and value, a draft's ids, reason or proposed rule, the provider, service, port and domain). Keys named like a secret are dropped and sizes are capped. No principal, prompt, credential, environment variable or fileThe sealed receipt: agent, action, target, verdict, the Artzain crew's votes, the bundle version, and the payload's SHA-256, not the payload
The sidecar reports a commitAfter a governed create or policy update commitsSandbox id, policy hash, decision id, methodThe projection that drift is checked against
HeartbeatEvery minuteSidecar and OpenShell versions, the installed registration's digest, what gateway.toml holds now, decision latency (p50 and p95), undelivered reportsThe latest values
InventoryEvery five minutes, and on changeEach sandbox's id, name, phase and effective policy hashCatalog rows, and drift findings
The sidecar fetches your base policyAt start, and every five minutesNothing. It receives your team's compiled base policy, its digest and the prover's resultNothing new
OpenShell's own telemetryOnly if you turn it onOpenShell's own data, to NVIDIA. ArtzAIn only writes your choice into the gateway's settingsNothing. The sidecar sends nothing to anyone but ArtzAIn
ArtzAIn reads OpenShell advisoriesHourlyNothing about you. ArtzAIn reads NVIDIA's published listA finding on each of your gateways running an affected release
Your host sends its break-glass journalAfter a break-glass window, once ArtzAIn answersEach window's length, reason and opener (your host's user name), and each write it let through: the method, the action and target ArtzAIn would have decided, the payload's SHA-256, the operation digest, the caller's subject id, kind and provider, and the request idFlagged receipts, not billed; a Review queue item for each window; each window's reason and opener, cleared when you delete your account

The sidecar logs no payload, target, key or body. It logs a failure by its kind, and logs how it reaches ArtzAIn without the credential.

05 · Credentials

Credentials and approvals

Two credentials, each with one job: a short-lived enroll token that connects a gateway once, and the gateway credential the sidecar uses after that.

The enroll token

  • cnxt_ and 256 random bits. It is valid for 30 minutes and works once.
  • ArtzAIn stores only its peppered SHA-256, and redeems it under a row lock, so two redeems at once yield one credential.
  • It is bound to the digest of the configuration you approved. A different configuration is refused, and the token is not spent. Issuing a new token retires the earlier one.
  • The command that carries it is four lines joined by &&. A failed download, or a script whose SHA-256 is not the pinned one, stops it before the token is exported or anything runs.
  • It goes to ArtzAIn over HTTPS only. Plain http:// is refused unless the engine runs on the same host.

The gateway credential

  • cnxg_ and 256 random bits: a key kind of its own, stored as a peppered hash. It never works as an account key.
  • It may call ten routes and no others: decisions, the commit report, announcing the gateway, and, for its own gateway only, its inventory, heartbeat, break-glass journal, base policy, revoke and the two key-rotation routes.
  • A decision must name the credential's own gateway as agent and as target. ArtzAIn refuses anything else before the engine runs, so nothing is sealed, whatever your account's identity-binding setting.
  • It does not expire. You rotate it (at most two live keys during a rotation) and you revoke it.

On your host

  • The credential lives in ~/.config/artzain/openshell/sidecar.env: mode 0600, in a 0700 folder. The sidecar's own token sits beside it.
  • systemd passes it to the sidecar as an EnvironmentFile. The sidecar's unit sets UMask=0077 and NoNewPrivileges=yes.
  • Your gateway's binary is asked its version without the credential in its environment.

Revocation

A revoke takes effect on the next call: the gateway's keys get 401. You can revoke three ways:

  • artzain connect openshell remove on the host, which also restores your gateway's files;
  • Revoke… on your dashboard;
  • deleting your account.

Approval

  • On a team, the owner or admin who asked for a connect cannot approve it.
  • An account on its own approves with a written attestation.
  • The approval is itself a sealed decision in your ledger.

06 · Threat model

What we defend against, and what remains

Each threat, the control as built, and what is left. We list the residuals plainly so you can weigh them.

ThreatControl as builtWhat remains
The enroll token leaks (shell history, a CI log, a screenshot)30 minutes, one use, bound to the approved configuration, passed by environment. You see where and when it was used, and can revoke.Within its 30 minutes, whoever runs it first binds a gateway of their own to that configuration. Spotting and revoking it is yours.
The gateway credential is stolen from the hostTen routes, its own gateway only. Rotate and revoke. A file only its owner reads, and never logged.Anyone with the host user's access can decide as that gateway until you revoke it. The credential does not expire.
A compromised host forges receipts for another gateway or tenantArtzAIn assigns the gateway id. Agent and target must be the credential's own gateway, checked before anything is sealed.None found
A local process talks to the sidecarThe socket is your gateway user's alone. The loopback routes need the sidecar's token. gateway_jwt when your gateway signs its calls.Without gateway_jwt, any process running as your gateway's user can call the socket. That user is the boundary.
The interceptor is removed from gateway.tomlThe heartbeat reports what gateway.toml holds. Anything but the installed registration raises a finding, in your dashboard's bell and by webhook. Policy drift is checked independently, sandbox by sandbox.A gateway started from another configuration file is not seen this way. Your host's administrator can always unbind their own gateway: ArtzAIn detects it, and cannot prevent it.
Automatic approval of policy proposals is turned back onAny approval mode but manual is denied at any scope, by a vote no bundle can soften. The connect command sets the gateway-wide mode to manual first.None found
Supply chainSee section 09.The connect script is checked by its SHA-256, not signed. The compatibility manifest is not signed yet.
ArtzAIn is down or slowEvery governed write is refused. Running sandboxes keep their policy. Your host's user can open a break-glass window of up to 240 minutes: a write ArtzAIn gave no answer for goes through, journaled first, and is sealed as a flagged receipt and reviewed once ArtzAIn answers. A deny, a review, a rate limit, a refused credential and a gateway-wide write still refuse.Writes in a window go unchecked until you review them. A host user who rewrites the journal before ArtzAIn sees it can leave writes out: a journal that stops continuing is flagged, and drift still shows each sandbox it changed.
Flooding denied writes to run up your billEach gateway has its own hourly bucket. Over it, the write is refused.Within the bucket, denials are billed like any decision.
Personal data in the principalNot sent: the decision carries no principal.Names, a draft's reason and a proposed rule are your own text, and are sent.

07 · Failure

When something fails, the write is refused

The registration that decides fails closed. The one that observes, which reports after the commit, fails open and never decides. Our conformance run proves each row below on a real OpenShell gateway.

FailureWhat your gateway is told
ArtzAIn's verdict is not allowdenyreview
PERMISSION_DENIED, with the decision id
ArtzAIn is unreachable, times out, or answers with an error, a redirect or something unreadableUNAVAILABLE: refused
ArtzAIn gives no answer, or a 5xx, while your host has a break-glass window openAllowed, once the sidecar has journaled it. It becomes a flagged receipt, and the window a review, when ArtzAIn answers
The gateway's hourly bucket is used upRESOURCE_EXHAUSTED, with when to try again: refused
The sidecar is downYour gateway refuses on its own, and will not start while its interceptor does not answer
A gateway-wide policy writeRefused by the sidecar, without asking ArtzAIn
A sandbox created without a policy while the base policy is unknownRefused
The commit report cannot be deliveredKept in the sidecar's journal and retried. The write has already committed

08 · Integrity

Integrity over time

Connecting once is not enough. ArtzAIn keeps checking that your gateway stays bound, and tells you when it does not.

  • A silent gateway. Five minutes without a heartbeat raises openshell_gateway_silent. The next heartbeat resolves it.
  • The registration. At each heartbeat the sidecar reads gateway.toml and reports the digest of the registration it finds, or that it is missing or unreadable. ArtzAIn compares that with the registration the sidecar was installed for, and raises openshell_registration_changed when they differ.
  • Drift. Each sandbox's effective policy hash is compared with what ArtzAIn last allowed.
  • OpenShell advisories. ArtzAIn reads NVIDIA's published security advisories hourly, and a gateway on an affected release gets a finding. Our response is due within two business days, with a conformance run on the fixed release.
  • doctor, which you run on your host, checks the registration, the socket's and the credential file's modes, the sidecar's token, the clock, and the route to ArtzAIn.

09 · Supply chain

Supply chain, and how to check it yourself

Every piece that reaches your host is pinned by its hash, and you can check each one yourself.

PiecePinned and checked
The connect scriptYour dashboard pins its version and SHA-256, and the command checks it before anything runs. The release also carries a .sha256 file beside the script.
uv, which the script runsIts version, and each build's SHA-256, are written into the script. A build with another hash is refused.
artzain and its dependenciesEvery wheel by its SHA-256, in an environment of the script's own: uv pip install --require-hashes --only-binary :all:. The artzain wheel carries PyPI provenance (PEP 740) from CogNEXUSlabs/cognexus-tools, workflow publish-pypi.yml, environment release.
The sidecar imageIts base image is pinned by digest, and every wheel installs by hash. It is signed keyless with cosign by sidecar-image.yml on a sidecar-v* tag, and carries an SPDX SBOM attestation. Built for amd64 and arm64.
OpenShellArtzAIn does not install it. The connect command checks the installed release is the approved one (same minor line), and our conformance runs pin the deb by SHA-256.
PublishingEvery release tag waits for a named approver before anything publishes.

Check it yourself

the connect script, against the SHA-256 we publish
$curl -fsSLO https://github.com/CogNEXUSlabs/cognexus-tools/releases/download/python-v0.6.42/connect-0.6.42.sh && echo '67741fc70c2893c1044026f86d58db290fd6c2b3bbf91f009a1622f5660c6e4b connect-0.6.42.sh' | sha256sum -c -
the sidecar image's signature
$cosign verify ghcr.io/cognexuslabs/artzain-openshell-sidecar@sha256:93a8d5dc055a2ab81a120d0692fa119fdd10c043319c81338432a2cc0eb63193 --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '^https://github\.com/CogNEXUSlabs/cognexus-tools/\.github/workflows/sidecar-image\.yml@refs/tags/sidecar-v[0-9]+\.[0-9]+\.[0-9]+$'
its SBOM attestation (114 packages, artzain 0.6.42)
$cosign verify-attestation --type spdxjson ghcr.io/cognexuslabs/artzain-openshell-sidecar@sha256:93a8d5dc055a2ab81a120d0692fa119fdd10c043319c81338432a2cc0eb63193 --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '^https://github\.com/CogNEXUSlabs/cognexus-tools/\.github/workflows/sidecar-image\.yml@refs/tags/sidecar-v[0-9]+\.[0-9]+\.[0-9]+$'
the artzain wheel's provenance on PyPI
$curl -fsS https://pypi.org/integrity/artzain/0.6.42/artzain-0.6.42-py3-none-any.whl/provenance

For the self-hosted engine's own images, see verifying a release on the install page.

10 · Figures

Figures

Taken from the code of artzain 0.6.42 and the hosted engine. A test holds each figure in our review pack to the code, so a change that makes one wrong fails our build.

FigureValue
Enroll tokencnxt_ and 256 random bits; 30 minutes; used once
Redeems from one address30 an hour
Gateway credentialcnxg_ and 256 random bits; no expiry; at most 2 live keys
Key rotations per gateway12 an hour
Governed writes per gatewayyour plan's hourly rate, at most 600 an hour; bursts up to 10 times the rate
Interceptor timeout (your gateway's)1500ms
Decision deadline (the sidecar's)1200ms
Heartbeatevery 60s; silent after 300s
Inventoryevery 300s and on change; at most 200 sandboxes
Gateway socket0600, in a 0700 folder
Answer the sidecar reads from ArtzAInat most 256 KiB
Decision payloadstrings cut to 500 characters, lists to 40 items, nesting to 8; 200000 characters in all
A review's approval, remembered24 hours, 1024 entries, in memory only
uv the connect script runs0.8.15, checked by SHA-256
Python of the connect environment3.12
Connect scriptconnect-0.6.42.sh, SHA-256 67741fc70c2893c1044026f86d58db290fd6c2b3bbf91f009a1622f5660c6e4b
Sidecar imagesha256:93a8d5dc055a2ab81a120d0692fa119fdd10c043319c81338432a2cc0eb63193 (artzain 0.6.42)
OpenShell release approved by default0.1.2
Our response to an OpenShell advisory2 business days

11 · Hosting

Where ArtzAIn runs

The engine, your ledger and the key that signs your receipts, as our infrastructure code defines them.

  • The engine runs on AWS in us-west-2.
  • Its PostgreSQL database is not publicly reachable, is encrypted at rest, keeps 7 days of backups, and has deletion protection.
  • The key that signs your receipts is held wrapped by a KMS key, with rotation on.
  • Your sidecar reaches the engine through Cloudflare, which ends TLS and applies the WAF and rate limits, and then a load balancer that accepts TLS 1.2 and 1.3. Cloudflare therefore sees what the sidecar sends, in transit.
  • Only our Cloudflare zone can reach the engine. Cloudflare presents a client certificate that our own certificate authority signed, and our load balancer refuses any connection without it (mutual TLS). A site in another Cloudflare account that points at our load balancer fails at the handshake, so nobody can route around our WAF and rate limits.
  • Our status page, status.cognexuslabs.ai, publishes the decision API's and connect's availability.

12 · Fixed in 0.6.41

What writing this review found, and fixed

Holding this review to the code found five gaps. We fixed each with a test that failed first, and shipped the fixes in artzain 0.6.41 and its sidecar image.

GapNow
The sidecar's loopback port answered any local caller, as your gatewayA sidecar holding a gateway credential needs its own token on every route but /healthz
The dashboard's command could go on after a failed download or SHA-256 checkIts lines are joined by &&, so a failure stops it before the token is exported
Removing the registration from gateway.toml raised nothingThe heartbeat reports what the file holds, and ArtzAIn raises a finding
The install on a deb or rpm host pinned artzain's version, not any wheel's hashEvery wheel is installed by its SHA-256
Smaller items: where a token was used from was not shown, and the address was kept after account deletion; the credential was in the version check's environment; plain http:// engines were acceptedEach fixed

A gateway you connected with artzain 0.6.40 or earlier keeps working, but does not get these fixes, or break-glass, until you run the current connect script (0.6.42) on its host again, as the gateway's user. It needs no new enroll token: up finds the credential it saved.

The run installs the hash-locked environment, gives the sidecar its token and its view of gateway.toml, and restarts the sidecar once, with your gateway. Until then, that sidecar does not report its registration, so ArtzAIn cannot tell you if it is removed.

13 · Open items

Open items, and the independent review

What is not done yet, said plainly.

ItemState
An independent review of enroll and credential handling
PlannedIts scope and questions are below.
Signing the connect script and the compatibility manifest
Not yetThe script is checked by its SHA-256.
gateway_jwt on deb and rpm hostsThe sidecar checks it when your gateway signs. The connect command does not turn it on.
Kubernetes, Homebrew, snap and Compose gateways
Not supported yetThe sidecar image is published for them.
Gateway credentials expireNo. You rotate and revoke them.
OpenShell leaves secret fields out of interceptor calls
Not verified by usThe sidecar strips secret-named keys itself.
The address a token was used fromKept while your account lives, and shown to you. Cleared when the account is deleted.

Independent review: scope

How a gateway gets and uses its credential:

  • issuing, carrying and redeeming the enroll token;
  • the gateway credential: minting, its route allowlist and binding, rotation, revocation;
  • the sidecar's local surfaces: the socket, the loopback routes and their token;
  • the connect script's install path.

Independent review: questions

  1. Can an enroll token be redeemed twice, or for a configuration other than the one approved?
  2. Can a gateway credential decide as, report for, or revoke another gateway, or reach any route beyond its ten?
  3. Can a local user who is not the gateway's user reach the sidecar's decisions?
  4. Can anything the connect script installs differ from what it names by hash?
  5. Does any log line, error message or answer carry an enroll token, a credential or the sidecar's token?
  6. On a team, can the person who asked for a connect also approve it?

Reporting a vulnerability

Email security@cognexuslabs.ai, and please keep an unfixed defect out of public issues. We acknowledge a report within 3 business days and aim for a fix and coordinated disclosure within 90 days. The full policy is SECURITY.md in the cognexus-tools repository.

Next step

Bring this page to your security review.

We walk your security team through it, and answer what it leaves open.