orchkaDocs

Security

What the system actually enforces, and where the deliberate limits are.

Tenancy

Every workflow, credential, execution and dead letter belongs to an organization. Server-side queries are scoped by organizationId, not by user, which is what lets teammates share a credential and what stops one org reading another's data.

Membership is checked per request. A user in two organizations sees each one's data only while that org is active, and holding the editor open in two orgs at once does not cross the streams.

Authentication

Email and password, plus GitHub and Google. Sessions are signed with BETTER_AUTH_SECRET, and better-auth refuses to boot in production without one.

Password reset emails a single-use link valid for one hour. Requesting a reset returns the same response whether or not the address has an account — whether someone has registered is not something an unauthenticated form should reveal.

Email verification is available but off by default. Turning it on is a deployment decision: it protects against someone registering an address they don't control, at the cost of locking everyone out if mail is misconfigured.

With verification off, an address belongs to whoever registers it first. If your deployment is public before you sign up, someone could claim an address listed in ORCHKA_ADMIN_EMAILS. Sign up immediately after deploying, or use GitHub/Google sign-in, which supplies a verified address.

Rate limiting

/api/auth/* is the only unauthenticated surface. It is rate limited — 100 requests per 10 seconds globally, 3 per 10 seconds on sign-in and sign-up — with state held in Redis, so the limit is shared across replicas rather than per-instance.

Credentials

Encrypted at rest with AES-256-GCM and decrypted only in the worker, at execution time. No endpoint returns a secret in plaintext. Access is checked by organization, and every resolution writes an audit row. See Credentials.

AI provider keys are deliberately not read from environment variables: a process-wide key would be shared by every organization on the deployment and would bypass both the encryption and the scoping above.

The agent code tool

The AI Agent's evaluate_expression tool runs model-generated JavaScript inside QuickJS compiled to WebAssembly, not in the host process.

A QuickJS context starts with no host bindings at all — no fetch, no process, no require, no timers — so an expression cannot read environment variables, reach the network, or touch the filesystem. It is capped at 1 MB of heap and interrupted after 1 second.

This matters because the expression is reachable by prompt injection from any untrusted node input, and the worker's environment holds the database URL and the credential encryption key.

Outbound requests

Workflow automation means fetching arbitrary URLs, but "arbitrary" stops at the private network. The HTTP Request node and the agent's HTTP tools reject:

  • loopback (127.0.0.0/8, ::1) and 0.0.0.0
  • link-local (169.254.0.0/16, fe80::/10) — which includes the cloud metadata address
  • private ranges (10/8, 172.16/12, 192.168/16, fc00::/7)
  • .internal, .local and .localhost hostnames
  • any scheme that isn't http/https

Set ORCHKA_ALLOW_PRIVATE_NETWORK=1 to opt out, if workflows genuinely need to call internal services.

This is a hostname and literal-IP check. A public hostname that resolves to a private address (DNS rebinding) still gets through. A stricter boundary would resolve the address and re-check it, or route egress through a proxy.

Admin surface

/settings/engine and the queue statistics are restricted to addresses in ORCHKA_ADMIN_EMAILS, which denies everyone when unset. The restriction matters because job scheduler ids embed workflow ids, so the stats would otherwise let any signed-in user enumerate every organization's workflows and schedules.

The link is hidden from the sidebar for non-admins and the page redirects them away. The control that actually enforces this is server-side on the data itself: the statistics procedure returns not found regardless of how the page is reached.

Known limits

Stated plainly rather than left implied:

  • No DNS-rebinding protection on outbound URL checks, as above.
  • No key rotation. CREDENTIAL_ENCRYPTION_KEY cannot be rotated without re-encrypting every credential by hand, and losing it is unrecoverable.
  • Email verification off by default, with the squatting window described above.
  • No secret scanning on workflow output. Node output is stored and shown in the execution viewer; if a workflow prints a secret, it is visible to anyone in that organization.

On this page