← ALL DOCS
DOCS · SECURITY REVIEWERS

Security overview

This document is for security reviewers. It covers what Cloud Certainty StateCraft protects against, how users are identified, how access is limited, where data and audio go and how they are encrypted. Everything described here runs in your own AWS account.

Threat model

StateCraft lets many people see, run and change AWS Step Functions workflows, by mouse, keyboard or voice. What it must prevent:

ThreatMitigation
A user sees or acts on state machines no one granted themTwo independent layers: grants checked by the API on every call, and IAM credentials that a per-request session policy limits to the granted state machines, their versions, aliases, executions and Map Runs, and the granted actions (see Permissions)
A user reads execution data they shouldn'tThe data permission is separate from view. Without it, StateCraft asks Step Functions for metadata only and removes inputs, outputs and causes; for state machines encrypted with a customer managed key, the user's AWS session has no decrypt permission either
Someone stops, redrives or deploys by mistake or on purposerun and deploy are separate grants and need a connection whose role allows them (Operate, Full); deploys refuse to overwrite a change made since the person opened the state machine; every start, stop, redrive, Map Run change, TestState run, deploy, version and alias change is audited with the person's email
A voice command or an AI proposal changes a workflow silentlyVoice and typed commands are a deterministic grammar whose every change shows at once in the graph and the code, with undo; assist proposals are shown as a diff and applied only when the person accepts; neither deploys by itself, and "deploy" asks for confirmation
Audio or workflow definitions leave the organisationVoice uses Amazon Transcribe streaming in your account by default, and the browser's speech service only when an administrator chooses it (the portal says where audio goes); assist uses Amazon Bedrock in your account; StateCraft stores no audio or transcripts
A bulk job keeps acting after someone loses accessThe bulk workflow rechecks the job owner's grants for every item and acts with the owner's own scoped session
StateCraft passes a powerful role to a new state machineiam:PassRole is allowed only to Step Functions, only for the roles in the stack's PassRoleArns parameter, and never for StateCraft's own roles; people without deploy can test a state only with the state machine's own role
Someone impersonates another userFederated sign-in; the API trusts only emails the user can't choose, and binds the bootstrap admin to an immutable ID (see Identity)
A stolen token is used from elsewhereOne-hour ID tokens, revocable 8-hour refresh tokens, optional IP allowlist checked on every call
StateCraft is used to change its own stackThe roles users act through have no IAM permissions other than iam:PassRole to Step Functions, and no CloudFormation, DynamoDB or StateCraft bucket permissions
Actions can't be tracedApplication audit log and execution records with who did what, plus SourceIdentity = <user email> on every assumed-role session, so CloudTrail in every account names the person behind each Step Functions call
Another account's role is assumed by a third party ("confused deputy")Spoke roles trust only the hub's API and bulk worker role ARNs and require an external ID and a source identity
An execution ARN from the browser points somewhere elseThe API never trusts an ARN from the browser: it parses it and checks its partition, account, Region and state machine against the request and the grants
Data leaves your organisationNo vendor service in the data path; the stack never contacts the vendor at runtime

Out of scope: a malicious administrator of the AWS account (they can change any IAM policy), and the security of the identity provider, of users' devices and browsers, and of the browser vendor's speech service when an administrator chooses it.

Controls

PrincipleControl
Strong identity on every requestEvery API route requires a Cognito ID token, validated by the API Gateway JWT authorizer (issuer and audience). Admin routes check the role on each call.
Least privilegeEach component has its own role with resource-scoped permissions (IAM summary). Users act only through roles under the IAM path /statecraft/, narrowed per request to their grants.
Context-aware accessIP allowlist checked on every call (Business and above); outside it, every route except the sign-in check returns 403 ip_not_allowed.
Short-lived credentialsSTS sessions of 15 minutes per user and connection. Voice sessions use a presigned Amazon Transcribe URL valid for at most 5 minutes, for one person.
Strong authenticationYour identity provider's MFA applies. Cognito-only users must use TOTP MFA and passwords of at least 14 characters. User-existence errors are suppressed.
Separation of dutiesAccess levels per account (Read, Operate, Full) and permissions per state machine pattern (view, data, run, deploy).
Hardened portalStrict CSP, HSTS, X-Frame-Options: DENY, Referrer-Policy: no-referrer, a Permissions-Policy that allows only the microphone and clipboard writing, for the portal itself, no source maps (Portal).
AuditabilityExecution records with the person, application audit log, API access logs, and CloudTrail in every account with the user's SourceIdentity.

Identity and authorization

The API trusts the Cognito ID token's signature, issuer and audience (checked by API Gateway), and then decides who the caller is from its claims. Roles, grants, drafts and the bootstrap admin are keyed on the email claim, so a user who could pick their own email could take over someone else's access. The API therefore accepts the email only when it is one the user can't choose; otherwise it refuses the caller with 403 forbidden on every route.

How the user signs inEmail trusted whenWhy
SAML: IAM Identity Center, Entra ID, OktaalwaysThe email is asserted by your directory and signed by the IdP. Map it from an attribute users can't edit (Identity providers).
OIDCemail_verified is trueMany OIDC IdPs let users or guests set their email. The stack maps the IdP's verification claim to Cognito's email_verified.
Cognito user pool useremail_verified is trueUsers are created by administrators and can't change their email.

The provider is read from the signed token, never from anything the client sends.

Bootstrap admin. BootstrapAdminEmail is always an admin, so it is bound to an immutable identity: the first trusted sign-in with that email records the caller's Cognito user ID, and from then on only that user is the bootstrap admin. Sign in as the bootstrap admin straight after deployment. Resetting the binding is described in the admin guide.

Permissions, grants and session policies

What a user can do is the intersection of two independent layers.

  1. The connection's IAM role sets the ceiling for everyone: the local role is limited by the stack parameters LocalAccessLevel (Read, Operate or Full), PassRoleArns and StateMachineKmsKeyArns; a spoke role by the same parameters of its own stack. StateCraft can never do more in an account than its role allows, and the account owner controls that role.
  2. StateCraft grants decide what each user may do within that ceiling: a subject (user or group), a connection, Regions, state machine name patterns and permissions (view, data, run, deploy). See the admin guide.

Grants are not only checked in code. For every request, the API calls sts:AssumeRole on the connection's role with:

  • SourceIdentity = the user's email. It can't be changed for the rest of the session, also in role chains, and it appears in CloudTrail for every Step Functions, CloudWatch, CloudWatch Logs and KMS call in the target account;
  • a role session name sc-<email>, also visible in CloudTrail;
  • a session policy (for non-admins) that allows Step Functions actions only on the ARNs of the state machines matching the user's patterns (the machines, their versions and aliases, their Standard and Express executions, their Distributed Map children and Map Runs), and only the actions of the user's permission: reads for view; reads with kms:Decrypt for data; plus start, stop, redrive, TestState, Map Run updates, iam:PassRole to Step Functions and kms:GenerateDataKey for run; plus create, update, delete, versions, aliases, tags and log delivery for deploy. If a user's grants are too many to fit the session policy size limit, the request is refused rather than widened.

Listing state machines and reading CloudWatch metrics have no resource-level permissions in AWS, so those reads use a separate, read-only session, and StateCraft filters the results by the caller's grants: a user never sees a state machine they have no grant on.

Data without data. IAM can't separate an execution's metadata from its data. For people without data, StateCraft asks Step Functions for metadata only (includedData = METADATA_ONLY, includeExecutionData = false) and removes inputs, outputs, causes and parameters from what it returns. For state machines encrypted with a customer managed key, AWS enforces it as well.

Your SCPs, permission boundaries and key policies still apply on top: StateCraft adds restrictions and never removes yours.

Voice and assist

PathWhat is sentWhat is kept
Voice with Amazon Transcribe (default)Browser → Amazon Transcribe streaming in the stack's Region, over a WebSocket URL the API signs for that person (5 minutes, signed by a role that may only start Transcribe streams, assumed with the person's email as SourceIdentity)Microphone audio while the microphone is onNothing in StateCraft; the number of sessions in usage. Amazon Transcribe may use content to improve AWS services unless your organisation has an AI services opt-out policy
Voice with the browser's speech service (only if an administrator chooses it)Browser → the browser's speech recognition; in Chrome and Edge, the browser vendor's web service, outside your AWS accountMicrophone audio while the microphone is onNothing in StateCraft. The portal tells people where their audio goes
Typed and spoken commandsIn the browser: a deterministic grammar turns text into changes to the draft or into navigationNothingThe draft's autosaved versions
Assist (Business, off until an administrator turns it on)API → Amazon Bedrock in your account (AssistRegion), Claude model AssistModelIdThe person's description and, for a change, the current definitionThe first 500 characters of the description in the audit log; the proposal only if the person accepts it into a draft

IAM summary

RoleAssumed byPermissions
API role (<prefix>-api-<region>)LambdaIts two DynamoDB tables; sts:AssumeRole, SetSourceIdentity and TagSession on roles under /statecraft/ only; start the bulk jobs workflow; bedrock-mantle:CreateInference on the assist model only (when AssistModelId is set); read the account's licences in AWS License Manager; its log group; KMS only through DynamoDB. No Step Functions access to your state machines of its own
Local role (/statecraft/<prefix>-local-<region>)API role and bulk worker, with a source identityBy LocalAccessLevel: Step Functions reads, CloudWatch GetMetricData, CloudWatch Logs reads for Express executions (Read); plus start, stop, redrive, TestState and Map Run updates, and iam:PassRole to Step Functions on PassRoleArns, never StateCraft's own roles (Operate); plus create, update, delete, versions, aliases, tags and log delivery (Full). KMS only through Step Functions (kms:ViaService), limited to StateMachineKmsKeyArns when listed
Spoke role (/statecraft/<RoleName>, other accounts)API role and bulk worker role (the hub's SpokeTrustedPrincipal), with the external ID and a source identityAs the local role, by its stack's parameters
Voice role (/statecraft/<prefix>-voice-<region>, with Voice = Enabled)API role, with a source identitytranscribe:StartStreamTranscriptionWebSocket only
Bulk workflow and worker rolesStep Functions and LambdaThe workflow (a Standard state machine whose items run as Express child executions of a Distributed Map) may invoke only the worker; the worker reads and updates bulk job items in the table and assumes connection roles as the job owner
Update notifier roleLambdaIts settings and audit items; publish to the update topic
Portal deployer roleLambdaDeploy the portal's Amplify app from one file in the artifacts bucket
Installer rolesECS (one-off task) and LambdaRun the installer task; read the account's licences; pull the image from the AWS Marketplace registry; write the code to the artifacts bucket

* resources appear only where AWS has no resource-level permissions (states:ListStateMachines, ListActivities, ValidateStateMachineDefinition, TestState, cloudwatch:GetMetricData, log delivery actions, License Manager), for PassRoleArns when you keep its default * (still only to Step Functions, and never StateCraft's own roles), and for KMS keys when StateMachineKmsKeyArns is empty (still only through Step Functions).

Data flows

FlowPathProtection
Sign-inBrowser ⇄ Cognito managed login ⇄ your IdPTLS; SAML signatures or OIDC; authorization code flow with PKCE
APIBrowser → API Gateway → LambdaTLS 1.2+; JWT on every call; CORS limited to the portal origin; throttled
Step FunctionsLambda → STS → Step Functions, CloudWatch and CloudWatch Logs in any connected accountTLS; per-request, per-user scoped STS credentials with SourceIdentity
Bulk jobsAPI → StateCraft's bulk workflow → worker Lambda → STS → Step FunctionsTLS; the job owner's scoped session, grants rechecked per item
VoiceBrowser → Amazon Transcribe streaming in your accountTLS (WSS on port 8443); a presigned URL for one person, valid 5 minutes
AssistLambda → Amazon Bedrock (Mantle endpoint bedrock-mantle.<region>.api.aws) in your accountTLS; only the description and the current definition
LicenceInstaller and API → AWS License Manager in your accountTLS. A read of the account's licences for the product
Code installAWS Marketplace container registry → installer task → artifacts bucketImage pulled by digest; every file checked against a SHA-256 manifest; written to a private, TLS-only bucket

What stays in your account

Everything: identities, connections and external IDs, grants, drafts (workflow definitions), saved inputs (which may contain business data), execution records, bulk jobs, usage figures and the audit log, as well as your state machines, their executions and their data. Cloud Certainty operates no service in the data path, and the stack never contacts the vendor at runtime. The AWS Marketplace container registry and the vendor's public bucket in us-east-1 (templates and the release manifest) are read-only distribution points used at install and upgrade, and, with UpdateChecks = Enabled, for a daily read of the public release manifest that sends nothing about you. Links to cloudcertainty.com in the portal are plain links, followed only when someone clicks them. The one path out of your AWS account is voice with the browser's speech service, and only when an administrator chooses it.

Encryption

  • In transit. TLS for every AWS API, Amazon Transcribe streaming and the portal (HTTPS only, with HSTS). The artifacts bucket denies non-TLS requests.
  • At rest. With KmsKeyArn, your customer managed key encrypts the DynamoDB tables, CloudWatch Logs, the update topic, the artifacts bucket and the bulk jobs workflow. Without it, AWS owned keys and SSE-S3 are used. Your state machines keep their own encryption; StateCraft uses their keys only through Step Functions, only within a person's data or run permission, and only the keys in StateMachineKmsKeyArns when you list them.

Portal hardening

Amplify Hosting serves the portal with these headers:

  • A strict Content-Security-Policy: no inline or third-party script, no eval, frame-ancestors 'none', and AWS endpoints allowed only for sign-in, the API and, for voice, the Amazon Transcribe streaming endpoint of the stack's Region.
  • Strict-Transport-Security, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, X-Frame-Options: DENY, and a Permissions-Policy that allows only the microphone and clipboard writing, for the portal itself, and turns off camera, geolocation, payment and USB. Browsers ask each person before the portal can use the microphone.

Graphs, timelines and charts are drawn in the browser; nothing is loaded from third parties. Tokens are kept in session storage, never in cookies; refresh tokens last 8 hours and can be revoked.

Audit

Every administrative change (roles, connections, grants, settings), every deploy, version and alias change, every execution started, stopped or redriven, every Map Run change, TestState run, bulk job, draft change and assist request is written to the audit log with the actor, source IP and target. Admins read it in Administration › Audit log. Events are kept for the plan's audit days: 30 on Starter, 90 on Team, 365 on Business and 3,650 on Enterprise. Execution records keep who started, stopped and redrove each execution for 30 to 730 days by plan. For each AWS call, CloudTrail in the target account shows the assumed-role session with sourceIdentity set to the user's email. Forward CloudTrail and the log groups to your SIEM.

Licensing

StateCraft reads its AWS Marketplace licence from AWS License Manager in your account: at install and in the API. Nothing about your usage or data is sent anywhere. Without a valid licence the installer stops and the API answers 402 licence_required. If License Manager is unreachable, or a licence that was active disappears (which bridges a move between plans), StateCraft keeps working for up to 72 hours from the last successful check. Your data and your workflows stay in place either way.

Shared responsibility

  • Keep BootstrapAdminEmail on a protected identity and enforce MFA at your IdP. Grant Admin to a second person or group.
  • Keep connection roles at Read unless people need to operate executions there, and at Operate unless teams deploy through StateCraft. Narrow PassRoleArns to your workflows' execution roles.
  • Grant groups rather than single users, with name prefixes per team (orders-*), and give data only to people who need to see execution data.
  • Keep voice on Amazon Transcribe, set an AI services opt-out policy if your organisation requires it, and choose the browser's speech service only if sending audio to the browser vendor is acceptable.
  • Review assist proposals like any code change before deploying them.
  • On Business and above, set the IP allowlist to your corporate egress ranges if users should only connect from managed networks.
  • Forward CloudTrail and StateCraft's log groups to your SIEM.

Something unclear or missing? Email [email protected].

Tell us what you have. We'll make it better and cheaper.

Send a short description of your current setup. We will tell you what we would improve, what it would cost, and where the savings are. A considered reply from an engineer, not a sales sequence.

TELL US ABOUT YOUR SETUP →