Your financial data is treated
like financial data.
Papilio is built on infrastructure where security is a constraint, not a feature. Here's exactly what that means in practice.
How the system is built
Papilio runs on AWS — API Gateway, Lambda, MongoDB Atlas, EventBridge, SQS, CloudFront, and S3. The architecture is deliberately serverless: there are no long-running servers to patch, no persistent processes to compromise, and no infrastructure that sits idle between requests.
The Lambda runtime is written in Go. This is a security-relevant choice: Go's strict type system with explicit unmarshalling means every field in a payment configuration document must be explicitly handled at compile time. There are no silent undefined errors in financial logic. If a field isn't there, the code doesn't run.
Infrastructure is provisioned with AWS CDK in TypeScript. Every resource is version-controlled, reviewed, and reproducible. There is no manual console configuration in the production environment.
CloudFront + S3 ← Dashboard (Flutter web)
│ HTTPS + JWT
API Gateway (CDK) ← Rate limiting · JWT authoriser · CORS
│
Go Lambda Runtime ← 10 functions · typed config · no state
│ │
MongoDB Atlas Provider Layer
EventBridge GoCardless · Stripe · Akahu
SQS
Secrets Manager ← Credentials never in code or env vars
Encryption
All data stored in Papilio is encrypted at rest using AES-256. This includes your entity records, payment history, configuration, and the immutable event log. MongoDB Atlas handles encryption at rest transparently — it operates at the storage layer, below the application.
All data in transit is encrypted using TLS 1.2 or higher. This applies to every connection in the system: browser to CloudFront, CloudFront to API Gateway, Lambda to MongoDB, Lambda to payment providers. There is no unencrypted path through the system.
Provider credentials — your GoCardless API key, your Stripe secret, your Akahu token — are stored in AWS Secrets Manager, not in environment variables or configuration files. Lambda functions fetch credentials at runtime via IAM role. The credentials are never logged, never serialised into a response, and never stored in the application layer.
Payment data
Papilio does not store raw card numbers, CVVs, or full bank account numbers. It never sees them. Payment credentials are handled entirely by the payment rail providers — GoCardless, Stripe, and Akahu — who are themselves PCI-DSS compliant.
What Papilio stores is the outcome and context of payment events: amounts, statuses, timestamps, provider references, and customer identifiers. This is the data needed to run your flows and answer your questions. It is not the data needed to initiate a payment to an arbitrary account — that requires valid provider credentials which only your configured flows hold.
When a payment flow runs, it reads the payment method reference from your configuration (a GoCardless mandate ID, a Stripe customer ID, an Akahu account reference) and passes it to the provider. Papilio orchestrates. The provider moves money. The distinction matters.
Tenant isolation
Every record in Papilio — every entity, every payment, every event — is tagged with a tenant_id. Tenant isolation is enforced at the database helper layer, not the application layer. This means it is not possible to accidentally query across tenants by omitting a filter — the helpers enforce the constraint structurally.
API Gateway routing is tenant-scoped: every endpoint path includes {tenantId} and the JWT authoriser validates that the calling credential has access to that specific tenant before the Lambda is invoked. A credential for tenant A cannot be used to access tenant B's data. This is checked before any business logic runs.
Our own team's access to production data is logged, requires multi-factor authentication, and is governed by IAM roles with minimum necessary permissions. No engineer has standing access to production data — access is requested and time-limited.
Access control
Papilio uses two authentication mechanisms:
Issued by AWS Cognito. Used for dashboard sessions and user-facing operations. Short-lived, automatically refreshed, validated at the API Gateway layer before any Lambda invokes.
Long-lived credentials for backend and SDK integrations. Scoped to a single tenant. Stored hashed — we cannot recover a lost API key, only rotate it. Rotation is immediate.
Rate limiting is applied at the API Gateway layer before requests reach application code. Brute-force attempts against the API are rejected before they consume Lambda invocations.
The audit log
Every event in Papilio is written to an append-only event log. Payment state transitions, flow executions, entity changes, configuration updates, webhook dispatches, query executions — all of it. The log is never updated and never deleted.
This is not a compliance feature that was added later. It is how the system works. The runtime writes to the event log as part of every operation. The log is the authoritative record of what the system did and when.
The practical consequence: when something goes wrong — a payment fails unexpectedly, a flow executes at the wrong time, a record is updated incorrectly — the full history is queryable through the same API you use for everything else. No digging through CloudWatch logs. No reconstructing state from application logs. The event log tells you exactly what happened, in order, with timestamps.
{
"event_id": "evt_01j2x9q8r4",
"tenant_id": "pap_tenant_001",
"type": "payment.state_transition",
"from": "processing",
"to": "completed",
"payment_id": "pay_01j2x8p7q3",
"amount": 45000,
"currency": "NZD",
"provider": "gocardless",
"timestamp": "2025-03-17T09:14:22.441Z",
"actor": "system:flow:monthly_billing"
}
Infrastructure
Papilio runs on AWS infrastructure across multiple regions. We use separate AWS accounts for sandbox and production environments — not separate environments within the same account. A misconfiguration in sandbox cannot affect production.
The production environment has no manual console access in the normal operating state. All infrastructure changes go through CDK and a reviewed deployment pipeline. There are no snowflake servers, no configuration drift, and no undocumented manual changes.
Automated backups of the MongoDB Atlas cluster run continuously. Point-in-time recovery is available. In the event of data loss, recovery targets are under 1 hour RPO for the production database.
In the event of a confirmed data breach, we will notify affected customers within 72 hours with the nature of the breach, data affected, and steps taken. We will also notify the New Zealand Privacy Commissioner as required under the Privacy Act 2020.
Responsible disclosure
If you discover a security vulnerability in Papilio, please report it to security@papilio.sh.
We will:
Please do not publicly disclose a vulnerability until we have had a reasonable opportunity to fix it. We will work quickly.
If you have specific security questions before signing up — particularly if you're evaluating Papilio for a regulated industry, or if you need to complete a vendor security questionnaire — email security@papilio.sh and we will respond directly.