Security model
How we protect your workspace and your secrets, and what code running here is allowed to do.
Accounts and access
Who can sign in, how long a session lasts, and what an API key can reach.
- Sign-in
All members must use multi-factor authentication (TOTP). Sign-up and sign-in include bot and fraud detection. The sign-in callback is checked against a state cookie.
- Sessions
Sessions use a random token in a
__Host-cookie (HttpOnly,Secure,SameSite=Lax). We store only the token's SHA-256 hash. The identity provider's refresh token is stored encrypted. Sessions expire after 30 days, or after 7 days of inactivity. - Role changes
Sessions check the member's role every 5 minutes. If the identity provider is unreachable, sessions keep working for 15 more minutes and then stop. Removing a member revokes their sessions. A role change applies from the member's next request.
- Cross-site requests
Dashboard changes are accepted only from
https://app.shiftlabs.org. - API keys
256 random bits. Each key has one workspace and one role, and expires after 1–365 days. We store its SHA-256 hash. The key is shown once. A key's role can't be higher than its creator's.
- Roles
viewer<developer<admin, checked on every route. See what each role can do. - Isolation
Each API query is scoped to the workspace of the credential used. The path and body can't change it. Requests for another workspace's trigger or run return 404.
- Failed sign-ins
Failed API authentication is rate limited per IP. After 10 failures in a minute, further attempts get
429.
Members · ridgeline-ops
3| Member | Role | Sign-in |
|---|---|---|
| dana@ridgeline.example | admin | MFA · TOTP |
| li@ridgeline.example | developer | MFA · TOTP |
| sam@ridgeline.example | viewer | MFA · TOTP |
Removing a member revokes their sessions.
Webhooks
A trigger URL starts a run. Deliveries must pass signature checks, rate limits and a size cap.
- Trigger URLs
Trigger ids are 128 random bits. Developers can rotate or revoke trigger URLs at any time. Viewers see them masked.
- Signatures
Each trigger can require a signature:
shiftlabs(a secret we generate),stripeorgithub. Timestamped signatures must be within 5 minutes. Replayed deliveries are dropped. - Signing secrets
Encrypted with AES-256-GCM and bound to their trigger. A generated secret is returned once. A secret you supply is not returned.
- Rate limits
Per edge location: 100 requests per 10 seconds per trigger and 600 requests a minute per IP across the API and webhooks. At the edge, an IP sending more than 150 requests in 10 seconds to the webhook host is blocked.
- Size
A webhook body is capped at 256 KiB.
- Alert webhooks
Failure alerts we post to your webhook are signed with your workspace's secret in the same
Shiftlabs-Signatureformat. The secret is shown once and can be rotated. See how to verify one.
Deliveries to hooks.shiftlabs.org/t/kQ7v…
signed: stripeReplayed deliveries are dropped. Deliveries without a valid signature are not queued.
Secrets and connections
Secrets are write-only. No endpoint returns a value. Your code can send a secret to another service only through a connection. Admins manage connections and set the exact hosts each one applies to.
- Encryption
Each workspace's values are encrypted with its own data key (AES-256-GCM, bound to the workspace, the secret's name and its version). The data key is encrypted with a platform key.
- Stand-ins
Your code gets a stand-in instead of the value. The gateway replaces it with the value only for the secret's hosts (exact names, no wildcards or IP addresses, https only), and only if the stand-in was created in the same run. A request with a stand-in to any other host gets a
403. Text that only looks like a stand-in is not replaced. - Connections
A connection holds an auth template set by an admin. The workflow must declare the connection, and the host must exactly match one on its list. The gateway applies the template to each request to those hosts. The template overrides anything the code set. Redirects are not followed for requests that carry a secret.
- Scrubbing
If a response contains a secret, or a reversible encoding of it such as base64 or Basic auth, it is replaced with
[redacted]before your code sees it. Readable secrets are scrubbed from logs. - Readable and sign
reveal()returns only secrets markedreadable. A secret markedhmaccan only be used to sign, throughsign(). Its value is not exposed to your code. - Audit
Each use or blocked use of a secret is recorded in the audit log, and so is each read of a run's payload or a workflow's deployed source, once per person per hour. Admins can read it at
/v1/audit.
Audit log · ridgeline-ops
admins only| Time (UTC) | Action | Subject | Host | Detail |
|---|---|---|---|---|
| 21:41:10.470 | inject | stripe | api.stripe.com | ["STRIPE_KEY"] |
| 21:36:02.881 | blocked | billing | api.stripe.com | connection billing is not declared for workflow refund-check |
| 21:30:15.303 | inject | stripe | api.stripe.com | ["STRIPE_KEY"] |
| 21:05:40.112 | connection put | stripe | ||
| 21:05:12.640 | secret put | STRIPE_KEY | version 2 |
Outbound requests
All outbound traffic from your code goes through our egress gateway as fetch requests. Raw TCP is blocked at deploy.
Outbound requests · last hour
5| Request | Status | Time |
|---|---|---|
| GET api.stripe.comvia connection stripe | 200 | 182 ms |
| POST hooks.slack.comno connection | 200 | 95 ms |
| GET 203.0.113.7no connection | blockedrequests to IP addresses are not allowed; use a hostname | 2 ms |
| GET api.stripe.com:8443no connection | blockedport 8443 is not allowed; use 80 or 443 | 2 ms |
| POST hooks.shiftlabs.orgno connection | blockedrequests to shiftlabs.org are not allowed from workflows | 2 ms |
- Refused
IP-address hosts, ports other than 80 and 443, and any
*.shiftlabs.orghost. - Attributed
Every request includes
X-Shiftlabs-Tenant: t_…andX-Abuse-Contact: abuse@shiftlabs.org. The tenant value is an opaque reference that doesn't reveal your workspace id. Connection templates can't set either header. - Logged
Each request is logged with its host, method, status, connection and outcome. You can view the log on each run.
- Capped
Outbound requests are capped per workspace and per host. See Limits.
Limits
Workspaces start on these free-tier limits. We can raise them, up to platform ceilings.
Usage · last 24 hours
- Runs
- 630 of 1,000
- Outbound requests
- 1,236 of 3,000
- Concurrent runs
- 4 of 10
- Workflows
- 4 of 10
| Limit | Free | When you reach it |
|---|---|---|
| Runs per 24 hours · concurrent active runs | 1,000 · 10 | The webhook returns 429. |
| Workflows · active triggers · secrets · connections | 10 · 20 · 25 · 10 | Creating another returns 403. |
| Engine dispatches per run · run lifetime | 50 · 168 h | The run fails. |
| CPU · subrequests per invocation | 2,000 ms · 50 | The invocation fails. |
| Outbound requests | 3,000 per 24 h · 30/min per host | The gateway returns 429. |
| Deploys · new versions per 24 hours | 20/min · 100 | The API returns 429. |
| Webhook body · workflow source · API JSON body | 256 KiB · 512 KiB · 16 KiB | The request is rejected with 413. |
| Logs per invocation | 500 lines · 4,000 chars/line | Further lines are not stored. Longer lines are truncated. |
Abuse
Report abuse to abuse@shiftlabs.org with the X-Shiftlabs-Tenant value from the request.
- Deploy scan
Every deploy is scanned. WebAssembly, crypto-mining code and raw sockets are blocked. You get a warning for hard-coded credentials and for URLs with IP addresses, unusual ports or
shiftlabs.orghosts. Other patterns are flagged for review by our operators. - Hourly checks
We check usage across workspaces every hour, including traffic concentrated on one host, failing or scanned targets, blocked requests and CPU use. New workspaces and workspaces whose current code is flagged have stricter thresholds. An operator reviews each flag.
- Suspension
When a workspace is suspended, its active runs are stopped, and webhooks, runs and API changes are rejected. You can still view and revoke triggers, API keys and members.
- Reporting
Send reports to abuse@shiftlabs.org with the
X-Shiftlabs-Tenantvalue from the request. Only we can match that value to a workspace. Report a vulnerability in shiftlabs itself to security@shiftlabs.org, as described in Reporting a vulnerability.
Reporting a vulnerability
If you find a security problem in shiftlabs, email security@shiftlabs.org. These are the rules for looking for one.
- What to send
The page, endpoint or feature affected, the steps to reproduce the problem, and what someone could do with it. Tell us which workspace you tested from. If you saw someone else's data, describe it; don't send it.
- Your own workspaces
Test only in workspaces you own or have permission to test. To test isolation between workspaces, use two accounts of your own.
- Other people's data
Don't access, change or delete data that isn't yours. If you reach some by accident, stop, don't keep a copy, and tell us.
- No disruption
No denial of service or load testing, no spam, and no social engineering or phishing of our staff or customers.
- Telling others
Give us reasonable time to fix the problem before you tell anyone else about it.
- Safe harbor
Research that follows these rules is authorized. We won't take legal action against you over it, and we won't treat it as a breach of the Terms of Service or the Acceptable Use Policy.
- Out of scope
Problems in services other companies run, even ones shiftlabs uses: report those to their owners.
Platform
Where runs execute, and how this site and the API are served.
- Region
Runs execute in US regions only.
- Transport
HTTPS only, TLS 1.2 or later, with HSTS. Plain HTTP requests get
403. The app, API and webhooks are each served only on their own hostname. - API responses
API responses are never cached and cannot be framed or content-sniffed.
- Telemetry
Run telemetry uses separate write-only and read-only credentials.
- This site
This site loads no third-party scripts, fonts or trackers, and uses a strict Content Security Policy.