Case files / PAN-088
Approvals signed on the phone
A phone app and a serverless backend for watching one AWS account and acting on it. The server runs nothing until the phone signs exactly what it showed, and checks the signature again before it runs. It cost about forty cents a month.
Control flow
- Poll: health every five minutes
- Alert: critical push, past Do Not Disturb
- Sign: a person, on a bound device (human gate)
- Recheck: the server verifies again
- Execute: a shell guard refuses the obvious
- Audit: every step recorded
Human gate: A device-bound signature over what the screen showed · Stack: Kotlin · AWS SAM · Python 3.12 · Cognito · DynamoDB · Seed species: Ember
Problem
Cloud trouble arrives at night. The person who has to decide what to do about it is usually holding a phone, not a laptop. The phone had to be enough to see the problem and approve a fix, and nothing else had to be enough.
How it works
A health poller runs every five minutes. An alert goes to the phone through Firebase on a critical channel that gets past Do Not Disturb.
The person reviews the proposed action on the phone and approves it with a key bound to that device. The signature covers what the screen displayed, so a doctored copy of the request fails. The server checks the signature again at execution. Commands go through SSM, behind a shell guard that refuses the obviously destructive before they reach the box. Everything is written to the audit log.
Sign-in uses Cognito with TOTP. The phone carries no AWS SDK.
Results
- About 35 to 50 US cents a month to run. The only real line is Cost Explorer at one cent a call.
- 14 arm64 Lambda functions and 5 DynamoDB tables.
- “Doctored copies fail closed” was verified live, against a development box.
What broke
A security review found 5 high, 20 medium and 15 low issues. The worst: the self-hosted edition’s instance metadata hop limit was 2, so any container on the box, including the internet-facing proxy, could fetch the instance role’s credentials and assume roles in every member account. Credentials now reach only the services that need them, through a volume the proxy cannot see. All findings are closed except one, which is a limit of IAM.
Other traps: the CLI’s default region was wrong, so commands quietly found nothing. IAM is eventually consistent. And a “should be denied” test is never aimed at a production box: if the policy is wrong, the test becomes the forbidden command.
Limitations
Single user. The app was sideloaded, never on a store. Parked: the self-hosted install was deleted and the development boxes terminated.
Next
Parked. A keyless, phone-only version across three clouds is planned.