IAM derivation
Why wisp never asks you to write an IAM policy by hand.
Every wisp app has one Lambda function per route, queue consumer, schedule, bucket event, or workflow — which means IAM can be genuinely least-privilege: each function's role only grants what that function's own code actually touches, not what the app as a whole might need.
How it works
-
The compiler walks each handler's body for calls on a known
Store/Queue/Bucket/Workflowinstance —orders.put(...),fulfillment.send(...),uploads.getObject(...),checkout.start(...). -
Each call maps to a concrete action:
Method Action store.get/.query/.batchGetreadstore.put/.updatewritestore.deletedeletestore.transactWritewrite+deletequeue.sendsend(registered as a queue's consumer) receivebucket.getObjectreadbucket.putObject/.presignedPutwriteworkflow.startinvoke -
Each action maps to concrete IAM actions —
store.readbecomesdynamodb:GetItem,dynamodb:Query,dynamodb:BatchGetItem;queue.receivebecomessqs:ReceiveMessage,sqs:DeleteMessage,sqs:GetQueueAttributes; and so on. -
Every statement is scoped to the specific resource's ARN. The only wildcard
Resourcewisp ever emits is the function's own CloudWatch Logs statement (logs:CreateLogStream/PutLogEvents, scoped to that function's own log group) — nothing else gets a"*".
Seeing it for yourself
wisp iamprints a table of every function and its exact derived permissions — a real audit artifact, not a summary.
What this catches automatically
Because permissions are derived from what your code actually calls, a function that only reads from a store never gets write access, and a function that doesn't touch a bucket at all gets no S3 permissions whatsoever — there's no shared "app role" to accidentally over-grant.
A known, documented gap
If a store has global secondary indexes, a function that queries it gets read access to all of that store's
indexes, not just the specific one a given .query() call targets — figuring out which index a call used would need
deeper call-argument analysis than the compiler currently performs. It's a real least-privilege gap, but a narrow
one: a read grant, not a write grant.

