Security and privacy
Chrysalis runs code written by other people and by an AI agent. None of that code starts with access to your keys, your other apps or your computer. It gets only what it needs, and every boundary on this page is checked by automated tests on every change.
#The short version
- Your API keys never leave the server. They are not sent to your browser, to apps, to plugins or to the agent, and not even the settings screen shows them again.
- Apps run in a sandbox. An app's page has no session, no network access, and no way to reach settings, the agent or other apps.
- Plugins run in a sandbox too. Plugins are the code that runs on the server. Each one only gets the permissions you approved, can only touch its own app's data, and can only reach the websites its manifest names.
- Updates ask before they take more. If a community app's update wants a new permission, a new website or new packages, it stops and shows you before anything changes.
- No telemetry. There is no Chrysalis account and no Chrysalis cloud. Your chats, characters and keys stay on the device running Chrysalis.
Speech requests and voice and model lists refuse redirects, so a speech endpoint cannot forward its saved key to another address. Configure the service's final API address in Settings > Speech.
#Why add-ons are the risk
In many apps, an extension is a script that runs in the same page as everything else. It can read what the page can read, call anything the page can call, and see every key the page can see. So one bad extension gets everything, and so does a good one that turns bad in a later update.
Chrysalis is built so that apps and plugins never run with that kind of access. Nobody has to check every line of an app for this to hold: the limits are enforced by the browser's sandbox and by the server, not by trusting the app to behave.
#Who can reach what
| Your API keys | This app's data | Other apps, settings, accounts | The internet | |
|---|---|---|---|---|
| An app's page | No | Through its own plugins | No | No |
| A plugin | No, the server makes model calls for it | Its app's data folder, with the fs permission | No | Only the hosts it lists, with the network permission |
| The agent | No | Yes, it edits your workspace | Your own apps; server settings and new accounts only with your approval | Public internet from its shell, until you turn it off |
#Your keys
- Keys are stored on the server in a credentials folder outside your workspace, in a file only the system account running Chrysalis can read.
- Keys are write-only. Settings shows whether a key is set, never the key, and no request to Chrysalis returns one.
- When an app or plugin wants a model reply, image or voice, it asks the server. The server adds the key, calls the provider, and hands back only the result.
- A key stays with the address it was saved for. If a connection's address changes, its key is dropped instead of being sent somewhere new.
- The agent's file tools, search and shell cannot see the credentials folder, and keys never enter your workspace history.
- App backups contain the app and its data, never keys.
#Apps
Every app's page runs inside a sandboxed frame:
- The frame has no session cookie and cannot read the rest of Chrysalis's storage. It cannot read or change other tabs.
- Its content security policy forbids network connections, form submissions and loading anything from other websites. Scripts, styles and images come only from Chrysalis.
- Everything an app asks Chrysalis for goes through a bridge that checks each request against a short list: the app's own plugins, generating text, images, speech and embeddings, its own stored files, and read-only lists like which models exist. The agent, settings, accounts, admin tools and other apps are refused.
- The server checks the same list again for every request the bridge passes on, so a mistake in one of the two copies is not a way in.
- An app file opened directly in its own tab gets the same sandbox.
- Chromium-based browsers (Chrome, Edge) from version 152 also block peer-to-peer (WebRTC) connections from app frames.
#Plugins
Plugins run in QuickJS, a small JavaScript engine compiled to WebAssembly, on a separate worker thread. There is no fetch, no Node.js, no file system and no way to start a program. A single call is limited to 10 seconds and 64 MB of memory; a plugin that runs over only takes down its own worker.
A plugin can only do what its permissions allow, and a plugin from a repository or a file only gets the permissions you approved:
| Permission | What it allows |
|---|---|
routes, tools | Answer its app's requests, and offer tools to models |
store | Its own small key-value store. Other plugins' stores are invisible to it. |
fs | Read and write its app's data folder. Paths that climb out of it, and links that point out of it, are refused. |
llm | Ask the server for model replies and embeddings on your connections |
network | Web requests, made by the server, to the hosts listed in the plugin's manifest and nowhere else |
zip, schedule | Read uploaded zip files, and run on a timer |
Network access is closed by default. The server re-checks every redirect against the plugin's list, and refuses a listed website name that resolves to your computer or your local network. A plugin that lists a local address outright, such as localhost for a tool running on your computer, is allowed to reach it, and the install screen points that out.
Permissions limit what a plugin can reach, not what it does with it. A plugin that can read an app's data and reach the network can send that data to the hosts it lists. The install screen says so when a plugin has both. Install apps and plugins you trust.
#Installing and updating apps
- You see it before it runs. Installing from the Store, a repository or a file first shows every plugin, its permissions and the hosts it can reach. Nothing runs until you confirm, and the confirmation is the approval.
- You get what you reviewed. If the repository changed between the preview and your click, Chrysalis refuses and asks you to look again.
- Updates ask before they take more. A community app's update stops if it would give a plugin a permission it did not have, let a plugin reach a host it did not list before, or install, change or remove packages. You see exactly what is new before anything changes. See What gets reviewed.
- Packages never run on the server. They install with their install scripts turned off, and apps are built inside a sandboxed frame in your browser.
- Links in a repository are not followed. Symbolic links are checked out as plain files, so a repository cannot point Chrysalis at files outside the app.
- Official means official. An app is marked official only when Chrysalis installed it from the Chrysalis maintainers' own repositories. That record is kept outside your workspace, so an app cannot claim it, and an app restored from a file is never official.
- You stay in control. Turn off or remove any plugin from the app's Plugins dialog, or delete the app.
#The agent
- The agent's command shell runs in WebAssembly inside your browser tab, not on the server. It cannot start programs on your computer.
- The shell can reach the public internet, never your computer or local network. Turn that off in Settings > Agent.
- The agent cannot read your keys, your account settings or your MCP server list.
- Changing server settings or creating an account shows you what will change, in Chrysalis's own words rather than the agent's, and waits for you to approve.
- MCP servers that run a program on your computer can only be added by an admin, in Settings. Nothing in a workspace can add one.
- Every file the agent writes is saved in your workspace history, so any change can be undone.
Like every AI agent, it can be misled by text it reads, such as instructions hidden in a character card or a web page. That is why the actions above wait for you. Read what it asks to do before you approve.
#Accounts and your network
- By default only the computer running Chrysalis can open it. The first account is created from a one-time setup link that Chrysalis prints where it started and in its log.
- Every account has its own apps, data, agent and keys. Only admins can change server settings or manage accounts.
- Passwords are stored as bcrypt hashes. Repeated wrong passwords are slowed down per account and per address.
- Changing or resetting a password signs that account out on every other device. Deleting an account ends its sessions.
- Requests that use an unknown host name are refused, which stops websites from reaching Chrysalis on your network through your browser. Writes from other websites are refused too.
To use Chrysalis away from home, prefer Tailscale over opening a port on your router. If you do put it on the internet, serve it over HTTPS and give every account a strong password.
#What goes online
Chrysalis only connects to the internet for things you set up or ask for:
- the model, image and speech providers you connect;
- installing apps and their packages, and the Store list;
- checking installed apps' repositories for updates when the launcher opens;
- checking for a new Chrysalis version when an admin opens the launcher or Settings > Server;
- websites that plugins with the
networkpermission list, and the agent's shell while its internet access is on.
#Backups and imports
An exported backup contains the app's data, so treat it like the data itself. When you import one, Chrysalis unpacks it into a separate folder, refuses files that try to land outside it, and shows you what it contains before installing.
#How it is tested
The claims on this page are checked by automated tests that run on every push and pull request:
- A deliberately hostile plugin, given every permission, tries to escape the sandbox, read files outside its folder, follow planted links, read other plugins' data, reach cloud metadata addresses and send data to hosts it did not list. The test fails if any of it works.
- Other tests try to drive the agent, settings and admin routes through an app's bridge, spell paths in ways meant to slip past the checks, borrow a key for a different address, and reach Chrysalis from a foreign website.
- Every security fix comes with a test that fails without it.
- The build and release workflows are pinned to exact versions and audited, and releases are built by those workflows, not on someone's computer.
#Good habits
- Install apps and plugins from people you trust, and read the install screen.
- Read update reviews instead of clicking through them.
- Keep Chrysalis up to date. Admins get an Update to X button on the Where to? page when a new version is out.
- Give each provider key a spending limit where the provider offers one.
- Do not share app backups publicly; they contain your chats.
#Report a vulnerability
Please report security problems privately through GitHub's security advisories, not in a public issue or on Discord. Include your Chrysalis version (chrysalis --version), how you run it, and the steps to reproduce. Fixes land in the next release, and only the latest release is supported.