# SquidShell E2EE agent vault · 0.1.0

Beta release. The E2EE transport API is available at `https://api.squidshell.com/v1`. Node.js 24+ is required. The dependency-free SDK/CLI is published on npm as `squidshell-sdk@0.1.0` (beta). It has not undergone an independent security audit.

Interactive human guide: https://www.squidshell.com/developers#vault. Browser demonstrations use fixed synthetic data, never real credentials.

## Quick start

Create a vault and an agent API key in My Page → API keys at https://www.squidshell.com/dashboard/api-keys. Select that vault and read/write permissions for provisioning. Use a separate read-only API key for consuming agents.

Install the published package with `npm install squidshell-sdk@0.1.0`. Registry: https://www.npmjs.com/package/squidshell-sdk. The command is `npx --no-install squidshell-vault` when installed in your project.

1. Supply `SQUIDSHELL_API_KEY` from your OS/CI secret store. Never paste it into prompts or shell commands. Copy the **non-secret vault UUID** from the dashboard.
2. Create the local encryption key (hidden password prompt):

   ```sh
   npx --no-install squidshell-vault key-init --vault YOUR_VAULT_UUID --key-file ./vault.key
   ```

   Use a strong password of at least 16 bytes. Back up the encrypted file and password separately. The server cannot reset/recover this key. Keep key files outside source repositories.

3. For unattended commands, inject `SQUIDSHELL_KEY_PASSWORD` separately from your secret store. Store a value through stdin without putting it in shell history:

   ```sh
   your-secret-provider read deploy-token | npx --no-install squidshell-vault put deploy-token --stdin --key-file ./vault.key
   ```

   `your-secret-provider` is a placeholder for your existing secret manager. Input is UTF-8 and is preserved, including trailing newlines.

4. Inject the value directly into a trusted program:

   ```sh
   npx --no-install squidshell-vault run --key-file ./vault.key --env DEPLOY_TOKEN=deploy-token -- node deploy.mjs
   ```

   No shell is spawned; SquidShell API credentials and key-password environment variables are removed from the child. The program receives only the requested vault values plus its usual environment. The program itself can still expose its environment, so do not run untrusted commands.

`list` decrypts entry names locally. `get NAME --stdout` explicitly reveals a value; prefer `run` for agent workflows. Do not feed raw values into model context or tool logs.

## SDK

```js
import { VaultClient } from 'squidshell-sdk';
import { unlockKeyFile } from 'squidshell-sdk/keyfile';

const local = await unlockKeyFile('/secure/path/vault.key', process.env.SQUIDSHELL_KEY_PASSWORD);
const vault = new VaultClient({
  apiKey: process.env.SQUIDSHELL_API_KEY,
  vaultId: local.vaultId,
  vaultKey: local.key,
});
const secret = await vault.get('deploy-token');
// Pass to the trusted integration; never console.log(secret).
```

## Sharing, rotation and recovery

Each vault has a random 256-bit encryption key independent of its API credentials. Give an agent its own scoped API credential and the encrypted key file/password through a trusted out-of-band secret channel. This release does not provide device pairing or server-mediated public-key sharing. All holders of a vault key can decrypt any snapshot they obtain for that vault; use separate vaults for separate trust boundaries.

Revoke the old API credential in the dashboard before rotating after compromise:

```sh
npx --no-install squidshell-vault rotate --key-file ./vault.key --new-key-file ./vault-next.key
```

The new file is saved before uploading the re-encrypted snapshot. Both files are retained. If the response is lost, try reading with the new file before retrying. Distribute the new file only to authorized agents, switch their configuration, and secure old backups according to your retention policy. A read/write API key can replace or rotate the entire vault. Concurrent changes fail with HTTP 409; the SDK never blindly retries an overwrite.

API revocation blocks subsequent server requests. Rotation protects future snapshots under the new key. Neither can erase plaintext or old ciphertext/key copies already obtained by an agent. Losing every copy of the key/password means permanent loss of the vault's contents. Recovery consists of restoring your encrypted key-file backup and password.

## Security boundary

AES-256-GCM, 96-bit random nonces, 128-bit tags. Entry names and values are encrypted together; the vault UUID, revision and key identifier are authenticated as additional data. The server sees the vault's display name, owner, size, revision, key identifier, timestamps and access metadata. Use a non-sensitive vault display name. Maximum plaintext snapshot: 1 MiB / 1,000 entries.

Local key files use AES-256-GCM with a random salt and scrypt (`N=131072,r=8,p=1`, about 128 MiB). Files are exclusively created with mode 0600; on POSIX, non-owner-readable files and symlinks are rejected. On Windows the password encryption still applies; OS ACL hardening must be checked separately. Password changes require creating a new encrypted copy through the keyfile API or rotating.

The API cannot decrypt or cryptographically validate an uploaded envelope; authorized writers must use this SDK. It validates format, scope, grants, revision, size and nonce reuse. TLS is required; localhost HTTP requires explicit test opt-in. Redirects are rejected. Error bodies are never echoed. The client detects a lower revision within its lifetime (or below `minimumRevision`); this is **not** a durable, multi-device anti-rollback protocol. A malicious server can replay an old valid snapshot to a fresh client without an independently stored revision checkpoint. Server denial of service and compromised authorized endpoints are outside this protection. JavaScript cannot guarantee wiping all plaintext string copies from process memory.

This is not an independent security audit or a claim of 1Password-equivalent security. Existing desktop SSH credential storage is unchanged.

## Validation

The SDK source test suite covers encryption integrity, binding, key files, local permissions and client transport behavior. A clean install from npm verified ESM imports, encryption/decryption, tamper rejection, encrypted key-file roundtrip and the CLI. Django's `termihub_agents.test_integration` exercises this SDK and CLI against a real isolated API with fixture data. No production secrets are used.

References: [Node Web Crypto](https://nodejs.org/api/webcrypto.html), [Node crypto](https://nodejs.org/api/crypto.html), [OWASP scrypt guidance](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html).
