Help
Security & data handling
How Arachne isolates tenants, sandboxes fix jobs, stores and protects tokens, and records an audit trail, and the limits it has today.
Tenant isolation#
- Every storage call carries exactly one scope: a tenant (organisation), a person, pre-authentication, or a named system purpose. The storage adapters check that every key a call touches belongs to that scope and refuse anything else, so a bug in a route cannot read another organisation's items.
- The organisation comes from your credential, never from a request parameter: from your browser session, or from your token's organisation. An id that belongs to someone else is answered as
not_found, without saying whether it exists. - Permissions are your live role, read on every request, for the web app and MCP alike.
- Shared caches never hold tenant data. The advisory-explanation cache is written only through a system scope with the advisory's public fields; your project findings are added per call and never cached.
The fix-job sandbox and egress#
Fix jobs never run in the API or worker Lambdas. Each job is one CodeBuild build:
- The worker Lambda claims the job and starts the build with only three values: tenant id, job id and lease owner. No credential is passed.
- The build starts its own Docker daemon, an
--internalDocker network with no route out, and an allowlisting forward proxy attached to both that network and the outside. - The job runner (orchestrator) refuses to run as root, clones the repository anonymously, and runs every install, build and test step in toolchain containers on the internal network, as an unprivileged user.
- The containers can reach only the package registries on the proxy's allowlist (exact host match: npm, PyPI, Maven Central, crates.io, RubyGems, NuGet, Packagist, the Go proxy, GitHub for git dependencies, Meteor). The proxy refuses link-local, loopback and other reserved addresses whatever a name resolves to, which blocks the cloud metadata and container-credential endpoints.
- The result is written back through the same storage ports, and the job runs only while it still holds the dispatcher's lease, so a late duplicate build does nothing.
Pushing the branch and opening the PR happen later, in the API, only for an approved verified job, and only with the reviewed file map.
What is stored#
| Data | Where | Notes |
|---|---|---|
| Account: email, display name, identity provider subject | DynamoDB | Email must be verified to sign in. |
| Organisations, memberships, roles | DynamoDB | |
Projects: name, owner/name, settings | DynamoDB | No source code. |
| Dependency inventory and findings | DynamoDB | Package names, versions, advisory ids. |
| Jobs and evidence | DynamoDB | Plan, gate results with short log tails, the diff of changed dependency files. |
| Usage ledger | DynamoDB | Keyed by salted hashes of your email and identity, so usage follows a person across accounts. |
| MCP tokens | DynamoDB | SHA-256 hash, label, created / last used / revoked times. Never the token. |
| Audit log | DynamoDB | Hash-chained, append-only. |
DynamoDB point-in-time recovery is enabled. Repository working copies exist only in temporary directories for the duration of a scan or job.
Tokens#
- PATs are 256-bit random values with an
arpat_prefix (for secret scanners). Only their SHA-256 hash is stored; lookup is by hash, so the raw value is never stored, logged or audited. A PAT prefix is never parsed as a JWT. - OAuth tokens are verified for signature (issuer keys, an algorithm allowlist with no
noneand no HMAC), issuer, expiry and audience or resource scope. A token minted for the web API is refused by MCP. - No token passthrough. Inbound tokens are never forwarded. GitHub calls use the deployment's own credential, held in Secrets Manager and limited in code: one allowlisted repository, an endpoint allowlist (no delete, merge, force-push, admin or workflow calls), branches only under
remediation/, never the default branch, no.github/writes, and caps on writes and open PRs. - Secrets are read at runtime from AWS Secrets Manager by name; there is no environment-variable secret backend.
Audit log#
Security-relevant actions are appended to a hash-chained audit log: project changes, job submissions, approvals and rejections, PR openings, MCP token creation and revocation, every MCP tool call (with its arguments, after validation) and every denied call. MCP calls are chained per person. Arguments are validated before they are audited, so oversized or malformed input is never persisted.
Request protection#
- One public hostname behind CloudFront. The API rejects any request that does not carry CloudFront's secret origin header, and API Gateway's default endpoint is disabled.
- Per-IP limits (with lockout after repeated auth failures) apply before any token work; per-person limits after.
- MCP request bodies are capped at 256 KiB; auth and tool work run off the event loop.
- Application responses carry a strict Content-Security-Policy (same-origin scripts and styles only),
nosniffandframe-ancestors 'none'; the static site (including these docs) is served with HSTS,nosniffandX-Frame-Options: DENY. Browser sessions use__Host-cookies and a CSRF token on every mutation.
Honest limits#
Known gaps
- The egress proxy is not exfiltration-proof. It sees only
host:port(HTTPS CONNECT), so code running during an install that holds its own credential could still push data to an allowed host such as github.com or a package registry. Closing this needs TLS inspection. - The fix-job build runs on one shared role. Per-job, tenant-scoped build credentials are designed but not built; the build container (not the sandbox containers) can reach the application tables.
- No S3 evidence store or audit anchor yet. The audit chain is not yet anchored to Object Lock storage, and very large job results cannot be offloaded.
- Tokens do not expire; rotation is up to you.
- No MFA on the Cognito user pool yet, and email/password is the only sign-in method.
- Cognito's built-in email sender is rate limited (about 50 emails a day account-wide) until SES is configured, which can delay verification codes.
Found a security issue? Please report it privately to the project owner rather than opening a public issue.