Security controls
Org admins set these under Security in the dashboard. Each is off until you turn it on, and every change is recorded in your audit log. Encryption of ticket content, at the end, is always on.
Passkey sign-in
When this is on, people open your org only after signing in with a passkey that checked their fingerprint, face or device PIN. Email links and Google, Microsoft or Yahoo sign-in still sign people in, but your org then shows a page asking them to sign in with a passkey. Agents are not affected; they use keys.
- You can turn it on only while you are signed in with a passkey yourself, so you cannot lock yourself out. The Security page lists members who have no passkey yet.
- Members without a passkey add one under Account, then sign in with it.
- Once any of your orgs requires passkeys, a person who has a passkey can add or remove passkeys, or delete their account, only from a passkey sign-in. So someone who gets into their email inbox cannot swap in a passkey of their own. Each added or removed passkey sends an email to the account.
- Someone who lost every passkey writes to support@stuckbot.com from the account's email address. Support checks the request and removes that account's passkeys, so they can add a new one.
- Approving an app over OAuth, such as an MCP client, also needs a passkey sign-in for that org.
Limit: the first passkey on an account can be added from any sign-in. Someone who controls a member's email before that member adds a passkey could add their own. Ask members to add passkeys before you turn this on.
Single sign-on (SAML)
People can sign in through your identity provider, such as Okta, Microsoft Entra ID, Google Workspace or any SAML 2.0 provider. StuckBot signs in only email addresses on domains your org has verified, so another org's identity provider can never sign in one of your people. Only owners can set it up or change it, because the identity provider decides who signs in as whom.
Set it up
- Verify your email domain. Add the domain under Security. StuckBot shows a TXT record such as
_stuckbot.example.comwith a value startingstuckbot-verify=. Add it at your DNS provider and press Check. Each domain belongs to one org, and subdomains count as separate domains. You can remove the record once the domain shows as verified. - Create a SAML app in your identity provider. Use the Entity ID (audience) and Reply URL (ACS) shown on the Security page. They look like
https://app.stuckbot.com/sso/<org id>andhttps://app.stuckbot.com/sso/<org id>/acs, and the Entity ID address also serves StuckBot's metadata. Send the email address as an attribute namedemail(ormail, or the standard email address claim) or as the NameID, and sign the assertion. AdisplayName, or first and last name attributes, set the name of new accounts. - Give StuckBot your identity provider's details. Paste its metadata XML, or enter its issuer (entity ID), its sign-in address (HTTP-Redirect binding, https) and its signing certificate. You can list up to 4 certificates while you rotate them. Changing these details later turns single sign-on off until a new test sign-in works and you turn it on again.
- Run a test sign-in. The test goes through your identity provider and shows the result on the Security page, including why a response was refused. It does not sign you in as that person.
- Turn it on. People then choose Continue with single sign-on on the sign-in page and enter their work email. Optionally let people from your domains who are not members yet join as viewers or members when they first sign in. Joining counts toward your plan's people limit.
Require it
When single sign-on is required, members open your org only after signing in through it, and they go through your identity provider again every 24 hours. Other sign-ins still work for their other orgs and for Account. Agents are not affected; they use keys. Approving an app over OAuth also needs a single sign-on sign-in for that org.
- You can require it only while you are signed in through your org's single sign-on yourself, so you cannot lock yourself out.
- Owners who signed in with a passkey can always open the org, so a broken identity provider or an expired certificate cannot lock everyone out. Members whose email is not on a verified domain cannot use single sign-on; the Security page lists them.
- If no owner can get in, write to support@stuckbot.com from an owner's email address. Support can turn single sign-on off.
What StuckBot checks
Sign-ins must start at StuckBot. The identity provider's answer must be signed by a listed certificate (SHA-256 or stronger), answer that exact request in the same browser, be addressed to your org's Reply URL and audience, come from your issuer and be within its time limits, and each answer works once. An answer that arrives without a request, from a tile in your identity provider's app portal, is ignored and the person is sent through a fresh sign-in instead. A sign-in started from a link on another site first asks the person to confirm the org, so no site can quietly sign someone into an account they did not choose. Encrypted assertions and single logout are not supported. Signing out of StuckBot does not sign people out of the identity provider, and removing someone there does not end a StuckBot sign-in already open for up to 24 hours; remove them under Members to cut access at once.
Automatic provisioning (SCIM)
Your identity provider can add people to your org and remove them when they leave, through SCIM 2.0. It manages only people whose email address is on a domain your org verified (see single sign-on above), and it never removes, turns off or changes the email address of an owner. Only owners can set it up.
- Verify your email domain under Security, as for single sign-on.
- Create a provisioning token under Security, Automatic provisioning. It is shown once and works for 365 days. You can have two at a time, so you can switch to a new one without a gap.
- Set up provisioning in your identity provider. Use the SCIM base URL
https://www.stuckbot.com/api/scim/v2(Microsoft Entra ID calls it the Tenant URL, Okta the SCIM connector base URL) and the token as a bearer token in the HTTP header. Use the email address as the user name (Okta: unique identifier fielduserName). Turn on creating, updating and deactivating users, and turn off group provisioning. - Pick the role for new people: member or viewer. Change anyone's role later under Members.
- Adding someone creates their StuckBot account if they have none and adds them to your org. Joining counts toward your plan's people limit, and an open invite for the same address is used up.
- Turning someone off or deleting them removes them from your org at once, as Remove under Members does: their dashboard access ends, and the keys and apps they added for this org stop working. Someone turned off stays known to your identity provider and comes back with the same role when turned on again. Single sign-on does not add them back on its own, also after a delete.
- Removing someone under Members also holds: your identity provider's next update does not add them back. They return only when it turns them off and on again, or when you invite them again.
- Name and email changes change the person's StuckBot account, which they also use for any other orgs. A new email address must be on one of your verified domains and not used by another account.
- StuckBot reads
userName,name,displayName,emails,activeandexternalIdand ignores other attributes. Groups, bulk requests, sorting and password changes are not supported. Filters work onuserName,externalId,emailsandidwitheq. - Provisioning requests count as API requests: your allowed addresses for agents and the API apply to them, and each org can send 600 a minute.
- Every change is in your audit log, marked as provisioning.
Allowed addresses
Two lists, each up to 50 addresses or ranges such as 203.0.113.7, 203.0.113.0/24 or 2001:db8::/48. Ranges must be /8 or narrower for IPv4 and /16 or narrower for IPv6. An empty list allows any address.
- Agents and the API covers every request made with an agent token, a key or an OAuth token, sign-ups through a connector, and the MCP server. Requests from other addresses get
403 ip_not_allowed, anddetails.ipshows the address StuckBot saw. - Dashboard covers your org's pages. People can still sign in from anywhere, and Account and switching orgs still work, but this org's tickets and settings open only from listed addresses. You cannot save a list that leaves out your own current address.
HTTP/1.1 403 Forbidden
{"error":{"code":"ip_not_allowed","message":"This org accepts agent requests only from its allowed addresses, and 198.51.100.4 is not one of them. An org admin can add it under Security in the dashboard.","request_id":"req_4f0c9a1e2b3d4c5f","details":{"ip":"198.51.100.4"}}}An MCP client that runs on someone else's servers, such as a hosted AI chat app, calls StuckBot from that provider's addresses. List those, or leave the agent list empty. The SDKs do not retry ip_not_allowed. If a changed address locks every admin out of the dashboard, write to support@stuckbot.com from an owner's email address.
Keeping closed tickets
Set a number of days from 7 to 3,650. A ticket and its handoff tickets are deleted together once every one of them has been closed for that long. Closed means completed, resolved when only the blocker was asked for, handed off, or cancelled. A chain with any open ticket stays.
Deleting removes the tickets with their packages, checks, evidence, files, updates, comments, activity, access requests, signals and webhook delivery records. It keeps:
- the audit log, which gets one line listing the deleted ticket numbers, while earlier lines stay as they were;
- ticket numbers, which are never reused;
- anything on your own data stores, which you delete there;
- copies in StuckBot's backups until those backups expire, though the ticket content in them can no longer be read once its key is gone (see below).
A change takes effect a day after it is saved, so a typo can be fixed before anything is deleted. After that StuckBot checks every 10 minutes.
Encryption of ticket content
Always on, with nothing to set. StuckBot encrypts each ticket's package, updates, comments, evidence and files, and the details and package a signal carries, before they reach its database or disk. A ticket and its handoff tickets share one key (AES-256-GCM). That key is stored wrapped by your org's key, and the org key is wrapped by a master key kept outside the database.
- A copy of the database alone, such as a database backup, does not show that content. Database backups leave the keys out. StuckBot keeps one separate copy of the keys, replaced every night.
- Deleting your org deletes its keys at once, and deleting closed tickets under Keeping closed tickets deletes the key of that ticket chain. Once the nightly key copy is replaced, within a day, that content can no longer be read from StuckBot's server backups, even before they expire. The off-site copy of the backups is deleted on its own schedule, within 30 days.
- Not encrypted this way: ticket titles, labels, check descriptions and the commands or addresses they check, names of people and agents, reasons given when a ticket is released, escalated or rejected, verifier decisions, access requests, activity history and the audit log. StuckBot needs these to list, sort and search tickets.
- StuckBot's servers decrypt content to show it to the members and agents allowed to read it. So this protects copies of the data, such as backups, not content against StuckBot's operator. To keep content off StuckBot entirely, use a data store.
Your own key
An org admin can have your own key service lock the org key instead of StuckBot's master key. StuckBot then keeps only the locked copy. Each time it needs the org key, it asks your service to open it and keeps the result in memory for 30 seconds to 5 minutes, as your service says. After a failed call it waits 5 seconds before asking again.
- Cut off access: stop your key service. Once its last answer runs out, StuckBot cannot show or add this org's ticket content, including its own check results. Requests that need it get
503 org_key_unavailable. Ticket lists and titles still load. Start the service again and everything opens as before. - Delete for good: delete every key file the service holds. No one can then read this org's ticket content, in StuckBot's database or in any of its backups. That includes you, so keep a safe copy of each key file until you mean to do this.
- Audit: your service logs every request StuckBot makes, with the time, address, action and key.
StuckBot's nightly key copy is replaced within a day of connecting your service. From then on it holds the org key only as your service locked it. Older off-site copies are deleted within 30 days.
Run the key service
It is part of stuckbot-datanode in the Python SDK (pip install stuckbot, version 0.5.0 or later) and uses only the Python standard library. Unlike a data store, StuckBot connects to it, so it must answer on public https at port 443. Put it behind a reverse proxy or give it a certificate, and let only StuckBot's addresses in: 172.234.213.99 and 2600:3c06::2000:92ff:fe9f:7894.
- Make a key file:
stuckbot-datanode keys new-kek /etc/stuckbot-keys/kek-1.key. It holds 32 random bytes. Keep a copy somewhere safe and offline. - Copy the settings from Security, Your own key into
/etc/stuckbot-keys/config.json:
{
"org_id": "your org id from the Security page",
"issuer": "https://www.stuckbot.com",
"listen": "127.0.0.1:8760",
"keys_file": "/etc/stuckbot-keys/grant-keys.json",
"kek_files": ["/etc/stuckbot-keys/kek-1.key"],
"cache_seconds": 300,
"audit_log": "/var/lib/stuckbot-keys/audit.jsonl"
}- Pin StuckBot's signing keys:
stuckbot-datanode keys trust --config /etc/stuckbot-keys/config.json. - Run it:
stuckbot-datanode keys serve --config /etc/stuckbot-keys/config.json, as a service with its own user. Addtrusted_proxieswhen a reverse proxy is in front, so the log shows StuckBot's address. - On the Security page, enter its https address and confirm. StuckBot asks the service to lock the org key and checks that it opens again. Only then does it switch, so a service that fails the check changes nothing.
Each request carries a token StuckBot signs with the same key as data grants. The service accepts it only for your org, for that action and that exact request body, for at most two minutes, and only once. StuckBot also keeps a fingerprint of the org key, so it refuses a key that is not the one your service locked, and never writes content under a wrong key.
Change or stop
- New key file: make one with
keys new-kek, put it first inkek_fileswith the old file still listed, and restart the service. Then choose Lock again on the Security page. After that the old file is no longer needed. - Move: enter the new address under Lock again while the old service still answers.
- Stop using it: StuckBot's master key locks the org key again. This needs your service to answer one last time.
HTTP/1.1 503 Service Unavailable
{"error":{"code":"org_key_unavailable","message":"This org keeps its own encryption key, and its key service at keys.example.com did not open it (Failed to connect to keys.example.com port 443). StuckBot cannot show or add ticket content for this org until it does. An org admin can check it under Security."}}Limits: while your service answers, StuckBot's servers hold the opened key, so this does not hide content from StuckBot's operator at that time. What it gives you is the power to cut off access and to make every copy unreadable. Direct use of AWS KMS, Google Cloud KMS or Azure Key Vault is not available yet.