Mortar — Bring Your Own Cloud (BYOC)
Status: design doc; first customer pilot planned. Aliyun + AWS only at first; GCP / Azure added when a paying customer asks for them.
BYOC lets enterprises run Mortar’s data plane in their own cloud account, while Mortar’s control plane orchestrates from our account. Mortar never touches customer data; we only issue credentials, push config, and read aggregate usage metrics.
This unblocks deployments at Fortune 500 / regulated industries (banks, hospitals, government) where data leaving the customer’s VPC is a non-starter.
TL;DR
- Data plane (the customer’s data) lives entirely in the customer’s AWS / Aliyun account: RDS Postgres, OSS / S3, Redis / ElastiCache, FC / Lambda, SLS / CloudWatch logs.
- Control plane (Mortar Center web UI + the orchestrator) runs on Mortar’s account. Issues credentials, pushes config, reads metadata. Has NO access to customer rows.
- IAM cross-account role — customer creates a
mortar-control-planeIAM role that Mortar’s account can assume. Scoped to: manage RDS / OSS / FC / Lambda resources. NOT scoped to: read row data, decrypt KMS keys, modify IAM. - Lower margin but unlocks contracts that require data residency in the customer’s account.
What lives where
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
│ Mortar's AWS account │ │ Customer's AWS account │
│ │ │ │
│ ┌───────────────────────────┐ │ │ ┌───────────────────────────┐ │
│ │ Mortar Center web UI │ │ │ │ RDS Postgres (data) │ │
│ │ Mortar orchestrator │ ─┼────┼─▶│ S3 buckets (data) │ │
│ │ Mortar billing service │ │ as │ │ ElastiCache (cache) │ │
│ │ Audit aggregator │ │sume│ │ Lambda (compute) │ │
│ └───────────────────────────┘ │role│ │ CloudWatch Logs │ │
│ │ │ │ Mortar binary (data plane) │ │
│ ┌───────────────────────────┐ │ │ └───────────────────────────┘ │
│ │ Aggregated usage metrics │ ◀┼────┼─ (CloudWatch metric streams) │
│ └───────────────────────────┘ │ │ │
└─────────────────────────────────┘ └─────────────────────────────────┘
Customer account (data plane)
- RDS PostgreSQL — Mortar’s primary DB. Customer chooses size, storage class, encryption settings.
- S3 buckets — file storage. Customer KMS keys.
- ElastiCache (Redis) — cache + rate-limit counters.
- Lambda functions — user-deployed compute. Customer’s IAM execution role.
- CloudWatch Logs — function output + audit-event stream.
- EC2 / EKS — runs the Mortar binary itself (auto-deployed by the orchestrator).
Mortar account (control plane)
- Mortar Center — the web UI + tenant management. No customer data; only metadata (which customer, what tier, what region).
- Orchestrator — issues commands to the data plane through the cross-account role. Stateless; redeploys without touching customer state.
- Billing — sums usage metrics, generates invoices.
- Audit aggregator — pulls aggregate metric streams (per-customer request counts, ledger entries, etc.); never raw rows.
IAM setup
Customer creates a cross-account IAM role in their AWS account:
# mortar-control-plane.tf
resource "aws_iam_role" "mortar_control_plane" {
name = "mortar-control-plane"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::<MORTAR_ACCOUNT_ID>:root" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = { "sts:ExternalId" = "<CUSTOMER_TENANT_ID>" }
}
}]
})
}
resource "aws_iam_role_policy" "mortar_control_plane" {
role = aws_iam_role.mortar_control_plane.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
# Provision + tear down Mortar's data-plane resources.
{ Effect = "Allow", Action = "rds:*", Resource = "*" },
{ Effect = "Allow", Action = "s3:*", Resource = "arn:aws:s3:::mortar-*" },
{ Effect = "Allow", Action = "elasticache:*", Resource = "*" },
{ Effect = "Allow", Action = "lambda:*", Resource = "*" },
{ Effect = "Allow", Action = "logs:*", Resource = "*" },
# Provision the Mortar binary host.
{ Effect = "Allow", Action = "ec2:*", Resource = "*" },
{ Effect = "Allow", Action = "ecs:*", Resource = "*" },
# Read aggregate metrics for billing.
{ Effect = "Allow", Action = "cloudwatch:GetMetricStatistics", Resource = "*" },
{ Effect = "Allow", Action = "cloudwatch:ListMetrics", Resource = "*" },
]
})
}
External ID is mandatory — prevents the confused-deputy attack where one Mortar customer’s tenant ID is used to assume role into a different customer’s account.
What we deliberately exclude from the policy:
kms:Decrypt— Mortar can’t decrypt customer KMS keys; data stays customer-readable only.iam:*— Mortar can’t elevate its own privileges or create new cross-account roles.rds:Modify*Tags/Add*Permissionon the actual database — we manage Postgres metadata but never run arbitrary SQL.
The Aliyun equivalent is RAM cross-account roles (AssumeRole); same
shape, different syntax.
Bootstrap flow
- Customer signs up via Mortar Center, picks “BYOC” deployment.
- Customer pastes their AWS account ID; Mortar Center generates an External ID + Terraform module.
- Customer applies the Terraform. IAM role created in their account.
- Customer pastes the IAM role ARN back to Mortar Center.
- Orchestrator assumes the role + provisions:
- VPC + security groups
- RDS Postgres (single-AZ for dev, Multi-AZ for prod tier)
- S3 buckets (
mortar-files-<tenant>-<random>, etc.) - ElastiCache cluster
- EC2 / ECS instance running the Mortar binary
- Orchestrator writes the binary’s
MORTAR_*env-vars from the provisioned endpoints. Customer’s Mortar boots and registers with Mortar Center. - Customer adds their domain to Mortar Center; CNAME points at the customer-account Mortar.
Tear down: customer revokes the IAM role. Orchestrator can no longer operate; customer’s existing infrastructure keeps running until they manually delete it. No “abandoned by Mortar” failure mode — the binary is statelessly configured + customer-owned.
Pricing impact
BYOC customers pay Mortar Center fees + their own AWS bill. No metered Cloud credits from Mortar — the customer’s cloud spend is the metered usage.
| Plan | Per month | Includes |
|---|---|---|
| BYOC Standard | ¥4 999 / month | Mortar Center, orchestrator, support email, audit aggregation |
| BYOC Enterprise | ¥19 999 / month | + dedicated SRE channel, SLA 99.9%, multi-region orchestration |
| BYOC Sovereign | custom | + on-prem variant (orchestrator runs inside customer’s perimeter) |
Lower margin than hosted Mortar Cloud (we don’t markup the cloud bill), but unlocks deals we’d otherwise lose entirely:
- Banks — every domestic state-owned bank insists on data not leaving their VPC / their data center. BYOC + Sovereign tier closes those.
- Hospitals — HIPAA / PIPL-medical equivalent require named control over data location.
- Fortune 500 R&D — proprietary trade-secret data; legal won’t approve hosted Mortar at any price.
- Government — government / enterprise customers, especially in regulated industries (electricity, telecoms), require sovereignty over the data path.
Cloud support matrix
| Cloud | Status | Notes |
|---|---|---|
| AWS | ✓ | Full BYOC support; reference customer in pilot. |
| Aliyun | ✓ | Cross-account RAM role pattern; targeting first Chinese BYOC pilot. |
| Tencent Cloud | planned | Same cross-account pattern as Aliyun. |
| GCP | ✗ | Waiting on first customer; Workload Identity Federation gap analysis. |
| Azure | ✗ | Same — waiting on demand. |
| Private cloud (vSphere) | planned | Sovereign tier customers; needs the orchestrator to run on-prem. |
Pilot acceptance criteria
- Cross-account role Terraform module published.
- Orchestrator assumes role + provisions RDS / S3 / ElastiCache / Lambda end-to-end against a fixture customer account.
- Bootstrap flow walks through Mortar Center without manual ops.
- Billing pulls CloudWatch metrics + invoices the per-tenant total.
- First paying BYOC pilot signed.