Last updated

Security

What keeps your code, your cloud credentials and your running jobs safe in the Clusy notebook and in Portal, in plain terms, including what we do not have yet.

How we write this page

Clusy makes two products. The notebook runs your code in a sandbox we rent. Portal starts machines in cloud accounts you connect, or runs jobs on computers you enrol, and moves running jobs between machines, which means it holds credentials to your clouds and copies of your programs’ memory. Portal also has its own accounts and sign-in, separate from the notebook’s. Those are different risks, so this page treats them separately.

We would rather understate than overstate. Everything below describes what is built and running, and the last section lists what we do not have yet. If you need a control that is not here, tell us at security@clusy.io.

Portal: how it is built

  • One service for every workspace. Portal runs as one service on AWS ECS Fargate, in an AWS account used only for Portal, in the United States (us-east-1), separate from the notebook’s. It keeps every workspace’s records in one Postgres database that Supabase runs for us, in a Supabase project used only for Portal, in the same region. None of Portal’s tables is in a schema that Supabase’s generated API exposes.
  • Workspaces are kept apart five ways. A scope inside the service refuses lookups outside the workspace. Postgres row level security is on and enforced for every table, so a query that does not name its workspace sees nothing. Every query made for a workspace filters by it. Each workspace has an encryption key of its own, and a storage prefix of its own, with every storage URL Portal hands out signed for one object, one method and one expiry. These keep workspaces apart from each other, not from the service itself. The service can name any workspace, and does its work across workspaces under a second database login that sees them all, so row level security catches a query that forgets its workspace rather than stopping the service. Portal’s server can open every workspace’s data, so a compromise of it would reach all of them.
  • Traffic enters through an AWS load balancer. Its certificate is issued by AWS Certificate Manager. It accepts TLS 1.2 and 1.3 on port 443, answers port 80 only with a redirect to HTTPS, and drops invalid header fields. The service accepts connections only from the load balancer and from its own tasks. Console pages send a Content Security Policy that forbids framing, X-Frame-Options: DENY and nosniff. Portal does not yet send an HSTS header.
  • No server to log in to. On Fargate there is no instance of ours to reach by SSH, and the service runs as an unprivileged user. Portal’s own secrets are kept in AWS Systems Manager Parameter Store, encrypted under a customer managed key in AWS Key Management Service, and read at start; the database owner’s credentials go only to the separate tasks that run migrations and operator commands, never to the service. The file system the service keeps job files on is encrypted at rest and reachable only over TLS. People who administer Portal’s AWS account can still open a logged shell inside the running service, as described below.
  • Your machine talks to Portal and to our storage. The console and its sign-in are at portal.clusy.io and use a browser cookie; the console refuses bearer tokens. The command line tool, the MCP server and coding tools speak HTTPS to gateway.clusy.io with a bearer token, and that address never reads a cookie. A project you upload and an output you download go straight between your machine and our object storage through signed URLs, not through Portal’s server. The software never connects to a machine Portal starts; enrolling a computer with portal machine add uses your own SSH to reach it. When you connect a cloud from the command line, it uses your own cloud login or a key you point it to, and sends Portal what the connection needs; it keeps no cloud credential.
  • Cloud machines dial out. A machine Portal starts opens one outbound connection to Portal’s server and keeps its own API on localhost; Portal opens no inbound port. On AWS and Google Cloud Portal refuses to run in a security group or network that allows inbound traffic, and it never adds an inbound rule. On Azure virtual machines it makes a network security group with no inbound rule. Virtual machines on AWS and Azure, Fargate tasks and, by default, Google Cloud machines still get a public IP address. Azure virtual machines and Lambda instances get a throwaway SSH key whose private half is discarded; on Lambda the firewall is the account’s own.
  • Your own computers run jobs without a sandbox. A computer you enrol runs Portal’s agent, by default as a lasting background service, which dials out to Portal, and an Owner approves it in the console by typing its pairing code. Jobs there run as the user the agent runs as: the user who installed it, or on a Linux system install a user of its own named portal. They have that user’s files and access, and all jobs on that computer share that user. Portal never powers the computer off.
  • Checkpoint bytes never pass through Portal’s server. A machine writes a checkpoint straight to object storage and the destination reads it straight back, each through short lived signed URLs, one per object, that the server mints. The server handles the metadata, the keys and the hand over. A job’s output stream does pass through the server, which writes it in parts to our object storage. Only the first 256 MiB of each job’s output stream is kept.

Portal: sign-in, sessions and tokens

  • Portal has its own accounts, separate from the notebook’s. You sign in on Portal’s own page at portal.clusy.io with a six digit code sent to your email, with Google or with GitHub; there is no password. Accounts are kept by Supabase Auth in Portal’s own Supabase project in the United States, which records each sign-in with the IP address and browser it came from. Your browser signs in with that store, then hands Portal the store’s short lived access token once. Portal checks it, does not keep it, and signs you in through its own OpenID Connect issuer with PKCE. Before an email code is sent, the page runs Cloudflare’s Turnstile bot check.
  • Portal asks its own account store whether your account is still active: at every sign-in, and before other protected requests unless it has an answer less than a minute old. While the store cannot be reached, a request that starts or spends anything is refused, and other requests may rely on an answer up to five minutes old. Every hour Portal also checks every account with a live session, sign-in or token. When an account is suspended or deleted, Portal ends its sessions, revokes its sign-ins and tokens, and shortly after stops the jobs it and its tools started and revokes the computers it enrolled. A notebook account is a separate account: deleting or suspending it does not end Portal access.
  • Console sessions use a __Host- cookie, Secure, HttpOnly and SameSite=Lax, of which the server keeps only a hash. They idle out after 12 hours and end after 7 days. Writes need a CSRF token and a matching origin. The sensitive ones need a Portal sign-in from the last five minutes or a six digit code that Portal’s account store emails you, among them connecting or removing a cloud, setting or deleting a secret, making a token, approving a sign-in or a machine, and changing settings or storage.
  • Signing in the command line tool or the MCP server needs an Owner or Administrator to approve it in the console, and gives a grant that lasts until it is signed out or revoked. Under it the tool holds an access token that lives 15 minutes, renewed with a refresh token that rotates on every use; reusing a refresh token that has already been spent revokes the whole grant. Agent tokens made in the console live 1, 7 or 30 days. Portal stores only hashes of sessions and tokens, so a copy of its database does not yield a working one.

Portal: your cloud credentials

  • Prefer the keyless methods. For AWS, Portal assumes a role in your account under an external ID that Portal issues for that connection and workspace, so no long lived key leaves your account. For Azure it uses a federated credential that trusts your workspace’s own identity at Portal, with assertions signed by a key of your workspace’s own. Google Cloud connections hold a service account key today, stored sealed, because keyless federation does not work from Portal’s hosted service. Modal and Lambda offer only API tokens; use a workspace or key made for Portal alone, so you can revoke it without touching anything else.
  • Sealed at rest. Every connection value, secret and job key is encrypted with AES-256-GCM, bound to the row it belongs to, under a data key of your workspace’s own. AWS Key Management Service makes that key and unwraps it only under your workspace’s name, and Portal’s server holds the unwrapped key only in memory. The console and the API never display a stored value, only which fields are set, and provider error messages are stored with your connection values blanked out.
  • Never sent onward. Credentials are used inside Portal’s server to call the provider. They are not sent to your machine, to a coding tool, or to the cloud machines Portal starts. A machine receives a token of its own, the job’s inputs, signed storage URLs, its job’s checkpoint key and the secrets the job names. One exception: for an Azure Container Apps job, Portal stores a registry credential in that job’s configuration in your subscription, so Azure can pull Portal’s image. It is the connection’s client secret or, for a federated connection, a registry token that lasts about three hours.
  • Least privilege in your account. The policy Portal asks for in AWS lets it read prices, quotas, instance types, spot placement scores and Cost Explorer totals across the account. It can start instances only from public images, only with Portal’s tag, in security groups it made in networks you tagged for it, and end only instances that carry the tag. It can also run Fargate tasks in its own cluster, run spot interruption tests from its own templates, and create the service linked roles AWS uses for ECS, EC2 Spot and those tests. It may create security groups and SSH key pairs that carry its tag, and add inbound rules to security groups that carry its tag, which its production code never does. It may pass its own two roles to its Fargate tasks and interruption tests, write to its own log groups, and change the policies of the execution role setup made for its Fargate tasks, only within a permissions boundary. It cannot create IAM roles. In Azure, Portal holds the Contributor role on one resource group that setup creates. In Google Cloud it holds a custom role on the project; Google Cloud cannot limit deletion by label, so Portal checks labels, names and ids itself before deleting, and we recommend a project made for Portal. There, machines run with no service account and no SSH key. Setting a cloud up from the command line uses your own cloud login and shows you every step before it runs.
  • Removing a connection deletes its stored values at once, though they remain in database backups for about seven days. It does not revoke the credential at your provider or remove what setup made in your account; do that in your cloud.
  • What people who run Portal can do. Portal’s server decrypts credentials in memory to use them, and its role in AWS may unwrap every workspace’s data key, so the people who administer Portal’s AWS account can reach what the server can. They can run operator commands against the database: list people and their email addresses, read any workspace’s audit entries with IP addresses, export a workspace’s records, add a person to a workspace in any role from their next sign-in, approve a computer waiting to join a workspace without its pairing code, freeze workspaces, revoke and restore a person’s access, and make an agent token of up to 30 days for any workspace. Such a token is recorded as approved by the workspace’s first active Owner, though that Owner took no action, and appears in its list of agents, where the Owner can revoke it. Each command writes an audit entry, and command logs are kept 400 days. They can also open a shell inside the running service, which holds decrypted credentials in memory; those sessions are logged and the logs kept 30 days. People Clusy authorizes as Portal operators can open an Admin page in the console with platform totals and the newest jobs outside our team, including how a failed job’s program exited; it shows no output streams, code or workspace names, and viewing it is not recorded in the audit log.

Portal: checkpoints and your data

  • A checkpoint is encrypted with a key made for that job, with AES-256-GCM and the object’s own name as associated data, before it leaves the machine. This is on by default; a workspace Owner can turn it off, and the choice is fixed for each job when it is created. The key is sealed on Portal’s server and given to the destination for the restore; it is never written to a machine’s disk or to a log.
  • Every blob carries a digest written as it is captured and checked as it is read, GPU tensors carry an exact checksum taken on both devices, and the whole state’s fingerprint is compared before the destination takes over. A mismatch aborts the move, the original keeps running, and the mismatched copy is kept one day for diagnosis.
  • Checkpoints live in object storage we operate, on Cloudflare R2 and in regional AWS, Azure and Google Cloud buckets, in the store Portal expects to be fastest for the job’s machine, so a checkpoint can be stored outside the United States when the job runs elsewhere. Uploads, outputs and output streams are kept in our main Cloudflare R2 bucket, whose location hint is eastern North America. All of it travels over TLS and is encrypted at rest by the storage provider, with our checkpoint encryption on top.
  • A suspended job’s checkpoint is kept three days, a period we set that cannot be changed from a workspace. About 18 hours before that, Portal marks the job as expiring, without emailing you; then the job fails as expired and its checkpoints are kept seven more days. When a job finishes or you stop it, its checkpoints are deleted; after other ends they are kept seven days. Outputs are kept seven days from saving and an upload no job took one day. A job’s own copy of its upload is deleted when the job ends, though copies of its working files may remain on the server’s file system and in its backups. Portal sets no expiry on a job’s output stream: it is kept with the job’s records for as long as the workspace exists, unless a storage rule removes it sooner, and removing a job from your history does not delete it.
  • Two things are honest limits. The key for a checkpoint is held by Portal’s server, so this is encryption against the storage provider and against a stolen bucket, not end to end encryption against us. And the working directory you upload, the outputs a job writes and its output stream are not encrypted by us beyond what the storage provider does.
  • Files whose names look like credentials, such as .env and the contents of .aws or .ssh, are left out of uploads by name, and so are JSON files that hold a private key; Portal does not otherwise look inside your files for secrets. Secrets a job needs are stored sealed and put in the job’s environment on the machine at start. Any token that may start jobs, a coding tool’s included, may start one that uses your stored secrets, and job code can print its environment; each use is recorded by the secret’s name. A checkpoint holds what the program holds in memory, so a secret the program has read can travel in its checkpoint.
  • Each machine that runs a job sends Portal’s server a description of itself, including its host name and the Python packages installed for the job, its CPU, memory and GPU use every five seconds, the job’s progress and its output stream. When a job fails, Portal records the error it ended with, including up to 500 characters of its message, and where in your code it exited.

Portal: what a coding tool can do

  • A coding tool reaches Portal through Portal’s MCP server or command line tool, under a sign-in you approve in the console or an agent token you make there. These carry scopes, read, jobs and connections, and can never carry admin; the approver can narrow the scopes a tool asks for and never widen them beyond their own role.
  • One route table on Portal’s server checks the scopes of every request, so a tool cannot do more than its token allows whatever its prompt says. A sign-in or token can carry a budget that caps what any job it starts may spend and that it cannot raise.
  • Within its scopes a tool needs no further approval from Portal. With the default scopes and no budget, it can start jobs that cost money in your connected clouds; move, resume, suspend and stop them; connect and remove clouds; set and clear spending caps; set and delete stored secrets; create codes that enrol a computer and remove enrolled computers; trigger a real interruption at a provider; and remove ended jobs from your history. Asking you before a launch, a spend, a connection or a deletion is something Portal’s skill and tool descriptions tell the tool to do; Portal’s server cannot check that you agreed. A connection has no spending cap and a job no budget unless someone sets one.
  • Some things are for people only, whatever the token: approving a sign-in, making an agent token, changing workspace settings and policies, running clean-up, approving a machine you enrol. A tool cannot approve its own sign-in, because approval needs a console session.
  • The MCP server tells a tool to treat script output, job names and log lines as data, never as instructions, and it blanks any value it read from your environment or a file out of that call’s answer and errors. It also accepts a key typed in as a plain value, as a last resort; a key given that way passes through the tool’s model. What a tool reads from Portal goes to that tool’s AI provider under your agreement with it; Portal itself calls no AI model.

Portal: the record

  • Every sign-in, approval, token change, membership change, operator command and every write to a workspace is recorded in an audit log, with who did it, on whose behalf and from which address. A write is recorded before it is carried out, and one that cannot be recorded is not carried out; a write that fails gets a second entry with its error. Reads are not recorded. The log is append only for Portal’s server: the database refuses the server’s updates and deletes on it, though those who administer Portal’s database could lift that. The writer refuses anything shaped like a token or a secret. Entries do not expire.
  • Inside your workspace, every launch, move, resume, policy decision, credential change and clean-up is an event you can read with portal explain, the console and the MCP tools.
  • Billing is checked in layers. Every machine is entered in a ledger before it is created and carries a hard deadline of its own, at which it powers off even without Portal. A reaper inside Portal’s server runs every minute. AWS spend is reconciled against the provider’s cost data daily, with drift over a dollar and twenty percent flagged. Lambda instances and Azure virtual machines keep billing after they power off until Portal’s server deletes them, so they can keep billing while Portal is down.
  • Portal’s server log records request paths but never headers, cookies or bodies; a few of its lines, such as rate limit refusals, carry an IP address. It and the load balancer’s access log, which records each request with its IP address, are kept 90 days. Portal’s AWS account has a CloudTrail trail with log file validation, kept 90 days, Amazon GuardDuty, S3 Block Public Access across the account, disk encryption by default, and a private image registry that scans images on push. Releases deploy from GitHub with short lived credentials, and the deploy role cannot decrypt with KMS or read secret values.

Portal: releases and the software on your machine

  • The installer, served from gateway.clusy.io, runs nothing until the whole script has arrived. It uses a uv already installed at version 0.13.4 or later, or downloads one pinned version of uv from its publisher and checks it against a checksum written in the script, and it checks the Portal wheel against a checksum written in the script. It runs as your user and refuses to run as root, except for a system install on Linux, which enrols the computer with a code. The checksums and the wheel come from the same server, so this catches a damaged download, not a compromised server. Releases are not yet signed, and macOS builds are signed ad hoc only.
  • Portal’s Python dependencies are pinned to exact versions in a file served beside the wheel and installed from PyPI. That file carries no hashes and the installer does not check it, and uv may also download a Python interpreter.
  • The TLS trust store the tool uses is built from a current certifi with the compromised eMudhra certificates, and the roots that can vouch for them, removed, and the tool refuses a uv older than 0.13.4, which removed one of those roots. It sends your credential only to the gateway it signed in to, and never through a redirect.
  • The tool contains no analytics or error reporting. Besides the requests you make, a release build checks gateway.clusy.io for a newer build at most once a day, without your credential; setting PORTAL_NO_UPDATE_CHECK turns that off, and nothing installs by itself. Signing in sends your computer’s host name, which names the sign-in in the console. Portal’s server records each write it receives, from any tool, as a product analytics event, as our Privacy Policy describes. The tool keeps your sign-in in ~/.portal/cli.json, readable only by your user and not in your system’s keychain; uninstalling does not sign you out.
  • Setup registers Portal’s MCP server with Claude Code and Codex. Set up from the one line on our site, it also writes a marked block into their user instructions telling them to prefer Portal for Python cloud and GPU work, and saves Portal’s skill. It approves no tool permission in advance. portal setup --no-prefer-portal removes the block, and the skill if you have not changed it. The skill tells a coding tool to check your cloud quotas and, with your agreement, to request increases with your own cloud login.
  • A computer you enrol reports its hardware, host name, a hashed machine id, the user its jobs run as and whether that user can use sudo without a password. Its sign-in is kept in a file readable only by that user.

The notebook

  • Code runs in a sandbox operated for us by a provider, one sandbox per project, as an unprivileged user. CPU sandboxes block inbound traffic and a short list of outbound destinations; the rest of the internet is reachable, which is why we ask you not to put production credentials in a sandbox.
  • Traffic is encrypted in transit with TLS. Object storage is encrypted at rest, and the database runs on a managed platform that encrypts stored data.
  • Credentials for services you connect are encrypted by us before they are stored, under a key held outside the database, and your Clusy API keys are stored only as hashes. Headers you configure for a custom MCP server are the one exception, so avoid high value secrets there.
  • Sign-in is protected by an anti-bot check and sessions use short lived tokens. Access to production data is limited to the people who need it, administrative actions are logged, and sensitive internal records are restricted at the database level.
  • Chat and tool inputs are checked by an automated, rule based detector for abuse, alongside your work rather than in its way. What is recorded and how appeals work is in the Privacy Policy.

What we do not have yet

So that nobody has to ask:

  • No SOC 2 and no certification. We have a SOC 1 Type II report, in which an independent auditor tested how Clusy’s company wide controls operated over a period. Customers and their auditors can ask for a copy at security@clusy.io under a confidentiality agreement. A SOC 1 report covers controls relevant to our customers’ financial reporting, so it is not a SOC 2 report or a security certification. We do not hold SOC 2, ISO 27001 or any other security certification, and we do not claim one.
  • No independent penetration test report and no bug bounty. Good faith research is welcome and covered by the safe harbour in our Terms of Use.
  • Portal releases are not signed, macOS builds are signed ad hoc only, and the dependencies the installer fetches are not checked against hashes.
  • No key you hold. Portal does not encrypt checkpoints with a key you hold, a workspace’s key does not protect it against a compromise of Portal’s server, and we cannot yet destroy one workspace’s key on its own. Customer managed keys, bringing your own bucket and an EU resident edition are not built. Running Portal inside your own cloud account is offered to organizations under a separate agreement, which sets out its controls; this page describes the hosted service.
  • No HSTS, no web application firewall and no fixed address. Portal does not yet send a Strict-Transport-Security header, its API answers carry no security headers, and no web application firewall sits in front of it; request limits are enforced in Portal’s own code. Portal’s server has no fixed outbound IP address, so you cannot limit its access to your cloud by address.
  • Portal has no second factor. You cannot enrol one, and the notebook account’s does not apply.
  • No self serve deletion, export or audit log for Portal. Portal has no way in the product to delete your account or export your records, and members cannot read their workspace’s audit log. Ask us by email, as the Privacy Policy describes.
  • No point in time recovery and no tested restore. Portal’s database is backed up daily and about seven days of backups are kept, so up to a day of changes could be lost, and we have not yet tested restoring a backup.

Your side of it

Security is shared. Keep your sign-in and your Portal tokens to yourself. Approve coding tools with the narrowest scopes that work, give agent tokens the shortest life and the smallest budget that work, and set a spending cap on each connection. A command line or MCP sign-in lasts until it is revoked, so sign out with portal logout on computers you stop using, and revoke sign-ins you no longer need in the console under Settings, Agents. Connect AWS and Azure through a role or a federated identity rather than a long lived key, and for Google Cloud, Modal and Lambda use a project, workspace or key made for Portal alone. Enrol only computers whose user you are content for jobs to run as. Where an integration reaches into your infrastructure, the controls on your side of the connection are yours. Tell us at security@clusy.io as soon as you suspect anything.

Responsible disclosure

If you find a vulnerability, email security@clusy.io with enough detail to reproduce it. We acknowledge reports within one business day, keep you updated, and coordinate disclosure once a fix is out. Please test only against your own account and data, avoid degrading the service for others, and keep nothing sensitive you come across. Our policy is also published at /.well-known/security.txt.

Need a security questionnaire or a conversation?

Email security@clusy.io with what you need and when. We answer questionnaires from what is on this page and from the systems behind it, and we will say plainly where a control is missing.