Capabilities
What the self-hosted build does and does not do: each capability's status, every module that runs with the IAM actions it makes, what is never read, and the versions it is tested on.
The capability matrix for the invitation-only self-hosted build: what ships, what is still in development, what is planned, and what is deliberately never done. The statuses use the public site's three words:
- available now — implemented and test-backed in the build. That is not a claim that a pilot has validated it.
- in development — implemented or partly implemented, with live verification or a production dependency still open.
- planned — a direction with no delivery date. It is not in the build.
Two tests in observability/internal/cli hold this page to the code.
TestTheCapabilityMatrixNamesWhatRuns checks that every module the scheduler
runs has a row here, that no row names a module it does not run, and that
every IAM action a collector makes is listed beside it.
TestTheCapabilityMatrixMatchesTheRolesGrants checks each role's section
against its CloudFormation template, in both directions: every action the
template grants is listed there, and nothing listed there is ungranted.
Scope
- Cloud: AWS, and Google Cloud projects in Estate (Compute Engine instances, GKE clusters, Cloud SQL instances, Cloud Storage buckets, VPC networks and subnets, load balancers, Cloud Run services, Pub/Sub topics and subscriptions, BigQuery datasets, service accounts, Firebase apps and Cloud Functions, read through workload identity federation), and Google Cloud billing in Spend: a billing account's BigQuery export, read daily. Azure subscriptions in Estate: the platform signs in and collects virtual machines, AKS clusters, SQL databases, PostgreSQL flexible servers, storage accounts, virtual networks and App Service apps, and Admin → Accounts connects a subscription; every collector and the reader role's exact reads were verified against a live test subscription. Azure spend in Spend: a subscription's Cost Management export, read daily. Azure provisioning through the library's templates, each run live on a test subscription. Import's generated template for an Azure resource and Azure templates of your own and Blueprints with Azure slots are in development.
- Deployment: self-hosted, with Docker Compose or the Kubernetes
kustomizations in
deploy/. A hosted service is planned, after the first self-hosted pilot. - Licence: offline, verified by the binary alone. The tiers are Essentials (Estate), Professional (Estate and Spend) and Enterprise (Estate, Spend and Provisioning, and the enterprise capabilities). Performance comes with Professional and Enterprise.
- Validation on a live account: one AWS account, the platform's own, was onboarded, collected, alerted on and billed end to end. That covers the external id's negative check, a status change delivered to a webhook, and a delivered Cost & Usage Report reconciled against Cost Explorer. A test estate was brought up and torn down through the product's own Provisioning templates, the agent applying them against that account as the reader role. Validation across separate customer accounts is in development.
Capabilities
| Module | Capability | Status |
|---|---|---|
| Estate | Inventory, derived health and the permanent status history | available now |
| Estate | The Dashboard: what needs someone now, ranked, each item linked to the page that explains it, then the estate by cloud and category, collection, spend against forecast, provisioning, recent changes and the hottest resources — the first page after sign-in | available now |
| Estate | Alerts, Slack and webhook notifications, and a public status page without identifiers — the front door for a visitor with no session | available now |
| Estate | Microsoft Teams, PagerDuty and email channels, each tested against a stand-in, email also against a real mail server; delivery to a live PagerDuty service and through a live mail relay is proven by the operator, with a step in the operator's manual | available now |
| Estate | Acknowledgeable alerts: a member acknowledges with a required note; the alert leaves the list until the resource changes again, and the acknowledgement is on the timeline | available now |
| Estate | Tag-defined application groups and declared apps, and freshness by each module's cadence | available now |
| Estate | Configuration diffs between snapshots on every resource's page, sensitive fields suppressed | available now |
| Estate | The change feed: configuration diffs, status transitions, acknowledgements, provisioning outcomes, cost anomalies and budget thresholds in one timeline per resource, account, app and team, ordered by the event's own time, with markers the customer's own systems post under a marker credential | available now |
| Estate | Ownership: one resolver deciding every resource's owning team from overrides, provenance, tags, the account and its relationships, with confidence and evidence, on every page that names an owner | available now |
| Estate | Impact and the investigator: one change and what moved around it — status, performance and settled cost on the resource and its neighbours — and, for a symptom, the changes before it ranked by time, distance and kind, each a candidate and never called a cause | available now |
| Estate | Tag policy: the tag keys a resource must carry and the values each admits, who complies with the money beside each answer, and at creation the agent stamps the team key, a request the policy cannot satisfy refused before it exists | available now |
| Estate | Findings: what needs fixing, grouped and prioritised, each priority showing its parts and its evidence | available now |
| Estate | Change alerts: a subscription to configuration changes that match a small predicate, judged as each read arrives, for a team or a declared app | available now |
| Estate | Relationships: a resource's neighbours — what contains it, what runs on it, what fronts and guards it, what provisioned it, the apps it belongs to, the team that owns it — each with evidence and confidence, drawn on its page | available now |
| Estate | Performance: latency, errors and saturation from CloudWatch with statistics, interactive charts and topology diagrams; EKS in depth, from the control plane to each pod | available now |
| Estate | Performance in depth: maxima, p95 and p99, a 30-day window read from an hourly rollup, and the hottest resources per signal with their pressure and a sparkline | available now |
| Estate | Performance for Google Cloud projects, from Cloud Monitoring: Compute Engine instances, Cloud SQL instances, Cloud Storage buckets, Application Load Balancers' forwarding rules, Cloud Run services, Pub/Sub topics and subscriptions, with the same statistics, windows and charts | available now |
| Estate | Performance for Azure subscriptions, from Azure Monitor: virtual machines, PostgreSQL flexible servers, SQL databases, storage accounts, App Service apps and AKS clusters, with the same statistics, windows and charts | available now |
| Estate | The inventory read every ten minutes, and the EC2 instances that start and stop between two reads recorded from EC2's own state-change events, with each run's start, end and lifetime in Changes | available now |
| Estate | Who made a configuration change: the CloudTrail events that may be it — principal, call and time, each a candidate and none chosen — on the change in Changes and in the investigator's evidence, for an AWS account whose administrator turns CloudTrail attribution on, within Cloud |
available now |
| Estate | Accounts: every connected AWS account, Google Cloud project and Azure subscription as an overview — what it holds by status and region, the state of its collection, the worst and stalest collector and when it was last read successfully — for any signed-in member | available now |
| Estate | Google Cloud projects: Compute Engine instances, GKE clusters, Cloud SQL instances, Cloud Storage buckets, VPC networks and subnets, Cloud Run services, Pub/Sub topics and subscriptions, BigQuery datasets, service accounts, Firebase apps and Cloud Functions, read through workload identity federation with a metadata-only custom role, every request field-masked | available now |
| Estate | Cloud Functions read from the Functions API's masked listing: every function under its own name whether or not it has run, with its state and labels, gone when the listing stops naming it | available now |
| Estate | Azure subscriptions: virtual machines, AKS clusters, SQL databases, PostgreSQL flexible servers, storage accounts, virtual networks and App Service apps, read by an app with no secret through federation and a custom role with no data action, connected from Admin → Accounts; every collector, and the role's refusal of content reads, verified against a live test subscription | available now |
| Spend | Cost Explorer ingestion with a 12-month backfill; Cost & Usage Report ingestion and amortization | available now |
| Spend | Tag allocation rules, inventory exceptions, anomalies, team budgets and Trusted Advisor recommendations | available now |
| Spend | Kubernetes attribution by the pods and nodes last seen, a current-placement approximation stated beside the figures | available now |
| Spend | Month-end forecasts with their range and measured error, on the tenant, team and account pages | available now |
| Spend | Reconciliation: a month's total from the places that must agree, per currency, with coverage and charge type beside it | in development |
| Spend | Cost change explanation: what moved between two periods, the movers summing to the difference, the changes in the same window beside them and never called causes | available now |
| Spend | Savings verification: a recommendation's saving split into usage and rate from the report's own quantities | available now |
| Spend | Showback out of the product: a page's table as CSV and a weekly digest per team by email | in development |
| Spend | Azure spend: a subscription's Cost Management export — FOCUS, or the ActualCost and Amortized |
available now |
| Spend | Google Cloud billing: a billing account's detailed usage cost export in BigQuery, read daily by the project holding it into the same cost tables, under the same authority rule, with labels attributed as tags, each cloud in its own currencies on the overview; Google's figures are its cost before credits, or after committed-use credits alone, and the Overview says which; its figures are in the same anomalies, team budgets and month-end forecasts as AWS's | available now |
| Provisioning | Versioned templates, typed inputs, approvals, provenance tags, drift detection and scrubbed execution logs | available now |
| Provisioning | The customer-run agent: a broker that holds the token and a worker that runs OpenTofu under another user | available now |
| Provisioning | An OpenTofu apply against a live AWS account: the test estate brought up and torn down through the product's own templates | available now |
| Provisioning | The library: built-in OpenTofu templates for the AWS types the inventory reads, checked by hash in the agent, enabled for the organisation, cloned to edit, with a notice when a newer release arrives | available now |
| Provisioning | Each library release runs the small Blueprint — a network, a database and a service — live in a test account, from request to destroy | available now |
| Provisioning | A role per template on AWS: with the setting on, each request runs as its template's own role, granted only that template's documented policy and the state bucket; a missing role fails the request, never falling back to the agent's identity | available now |
| Provisioning | The catalog: category, then type, then template, with the accounts whose agent can run each | available now |
| Provisioning | Blueprints: several templates requested as one run across slots of accounts, one approval bound to the run's expressions, each step a request, resume after a failure and destroy in reverse | available now |
| Provisioning | Import: a live resource recorded under a template version that declares its type and a team, with its provenance, its owner and its history; nothing is written to the cloud, and drift is not computed until it is fully managed | available now |
| Provisioning | The agent on Google Cloud: an agent token for a project, Google provenance labels and state in a Cloud Storage bucket | available now |
| Provisioning | An agent installed in one step for an AWS account the platform already reads, through a Cloud |
available now |
| Provisioning | Making an import fully managed: an import-only apply into the agent's state, then a separately approved retag, and release from state | available now |
| Provisioning | Import of a resource no template in the catalog declares, on AWS and Google Cloud: the account's agent generates its template, kept as a private version outside the catalog, and runs it as that resource type's own runner | available now |
| Provisioning | Google Cloud templates in the library: storage, messaging, networking, identity, compute and clusters, serverless and databases | available now |
| Provisioning | Azure templates in the library — a private storage account and one for archives, a virtual network with private subnets and one behind a NAT gateway, a private Linux virtual machine and a Spot one, a function app for background work and one for HTTPS callers in named address ranges, a PostgreSQL flexible server for development and a highly available one, a serverless SQL database for development and a zone-redundant one, a private AKS cluster and one answering named networks — each in an existing resource group, each run as its own managed identity with a token of its own, holding only its template's actions, in the subscription the request is for, with OpenTofu state in a storage container of the customer's; each run live through the product on a test subscription | available now |
| Provisioning | Import's generated template for every Azure type Estate collects | in development |
| Provisioning | Azure templates of your own, from git or inline, and Blueprints with Azure slots: a request runs on an agent whose runners are scoped to resource groups, and waits, naming the setting, until the account's agent is; an inline source is checked at publish for what the agent refuses — its own sign-in, a renamed provider, provider registration, a resource group made, OpenTofu state read | in development |
| Provisioning | Blueprints in the library: a network, a database and a service; Kubernetes with an Istio mesh across clusters | available now |
| Provisioning | A service account per template on Google Cloud, impersonated by the agent, its custom role only the template's documented policy and its own part of the state bucket | available now |
| Provisioning | Runners for templates of your own: the AWS, Google Cloud and Azure runners modules make one per template, scoped as the library's are, and a template of yours never runs as a library runner | available now |
| Provisioning | Stopping an EC2 instance through an approved request, from its resource page, verified by the next complete collection | available now |
| Provisioning | An expiry on a request that opens its destroy request | planned |
| Provisioning | In-place updates of a provisioned stack | not in v1 |
| Platform | Users, roles and teams; sessions with a per-user cap; step-up for destructive administrator actions; the audit trail | available now |
| Platform | Connecting an account from the portal: register an AWS account, a Google Cloud project or an Azure subscription, with the template and the exact commands to run in the customer's cloud (an AWS account's with its external id), verification before it is enabled, collection paused and resumed, one collection now, and removal — the CLI is needed for none of it | available now |
| Platform | The Admin area — the organisation, the audit log and an overview of the licence, the people and the accounts — and each user's own settings: profile, sessions, notifications, preferences | available now |
| Platform | Team scope: a member sees the spend, the anomalies, budgets and forecasts, the change feed and the provisioning requests of their own teams' accounts, and no other team's; the inventory and its status history are every member's (PLATFORM. |
available now |
| Platform | First run: a checklist from connecting an account to the first request, a guided tour over the real pages, and a Help menu | available now |
| Platform | Backups and a restore that does not replay cloud changes (the runbook, "Backing up and restoring the database") | available now |
| Platform | Single sign-on through the organisation's OpenID Connect provider (Entra ID, Okta, Google, IAM Identity Center), beside passwords or enforced with break-glass administrators; with the Enterprise licence's sso capability |
available now |
| Platform | Directory group sync: teams bound to the provider's groups, membership following the directory | available now |
| Platform | One home identity: with more than one cloud, the platform signs in at one and the others federate from it, with AWS or Google Cloud as the home | available now |
| Platform | Azure as the home identity | in development |
| Platform | SAML single sign-on and SCIM provisioning | planned |
| Platform | The AI reviewer channel: alerts and cost anomalies handed to an operator's own reviewer as claim checks, its answer on the subject and in the inbox, with evidence the tenant can see; sent from the page that asked | available now |
| Platform | The reviewer service itself, run by the operator, with a local model first (the owner, 2026-10-08) | available now |
| Platform | Triage: a small model the operator runs beside the platform decides for each status change, cost anomaly, budget, provisioning event, change and finding whether it needs people, the AI reviewer or nobody; shadow first, routing unchanged, every decision recorded with its confidence and its agreement with what people did, per kind in Admin | available now |
| Platform | Triage routing events by its decision, within floors and thresholds, once a kind is switched to enforce | in development |
| Platform | Usage that departs from a resource's own history, raised as an event for Triage | available now |
| Platform | Read-only API tokens for the API's GET routes | available now |
| Platform | The operator's manual, served to administrators inside the product | available now |
The reader role
deploy/cloudformation/reader-role.yaml, deployed in every
connected account. It grants exactly the actions in the three lists below,
and no wildcard. None of them returns S3 object contents, log events,
CloudFront configurations or Lambda source, each of which can hold a
customer's data or credentials. One returns the metadata of API calls:
cloudtrail:LookupEvents, CloudTrail's event history — who called what, on
which resource, and when — read for the one resource that changed, between
its two observations, and only for an account whose administrator turned
CloudTrail attribution on (the owner, 2026-10-07). Of an event the
platform keeps its id, name, time and principal, never its request, its
response or its source address, and it reads no Lake query, trail bucket or
data event. The template lists the reasons, and its test holds the
allowlist. Its trust policy lets the platform assume
it with the account's external id (sts:AssumeRole), set its source identity
(sts:SetSourceIdentity) and tag its session (sts:TagSession).
Called by the inventory collectors
Most run per account and region. S3's and CloudFront's resources have a global identity, so those two run once per account. CloudFront's distributions and Lambda's functions are found from the metrics each publishes, not from their configuration, which carries the origins' custom headers and the functions' environment variables.
| Module | Resource | Tier | IAM actions |
|---|---|---|---|
aws. |
EC2 instances | standard | ec2:, ec2: |
k8s. |
Kubernetes workloads on EKS | standard | eks:, eks: |
aws. |
EKS clusters | standard | eks:, eks:, eks:, eks: |
aws. |
RDS instances | standard | rds: |
aws. |
S3 buckets, per account | slow | s3:, s3:, s3:, s3:, s3:, s3:, s3:, s3: |
aws. |
DynamoDB tables | standard | dynamodb:, dynamodb:, dynamodb:, dynamodb: |
aws. |
Load balancers and their targets | standard | elasticloadbalancing:, elasticloadbalancing:, elasticloadbalancing:, elasticloadbalancing: |
aws. |
SQS queues | standard | sqs:, sqs:, sqs: |
aws. |
SNS topics | standard | sns:, sns:, sns:, sns: |
aws. |
API Gateway APIs | standard | apigateway: |
aws. |
AWS Backup vaults | standard | backup:, backup: |
aws. |
ACM certificates | standard | acm:, acm:, acm: |
aws. |
NAT gateways | standard | ec2: |
aws. |
CloudFront distributions with traffic in the last two weeks, per account, by id; their status unknown | standard | cloudwatch: |
aws. |
Kinesis data streams | standard | kinesis:, kinesis:, kinesis: |
aws. |
Lambda functions invoked in the last two weeks, by name; their status unknown | standard | cloudwatch: |
aws. |
ElastiCache clusters | standard | elasticache:, elasticache: |
aws. |
OpenSearch Service domains, by name and engine; their status unknown | standard | es:, es:ListTags |
aws. |
Glue jobs, by name; their status unknown | standard | glue:, glue: |
aws. |
Athena workgroups | standard | athena:, athena: |
aws. |
EBS volumes and their status checks | standard | ec2:, ec2: |
aws.ec2.vpc |
VPCs | standard | ec2: |
aws. |
Subnets | standard | ec2: |
aws. |
Security groups, their rules counted | standard | ec2: |
aws. |
Elastic IP addresses | standard | ec2: |
aws. |
VPC peering connections | standard | ec2: |
Called by the other jobs and by onboarding
| Caller | IAM actions |
|---|---|
cost., cost., cost. |
ce:, ce: |
cost. |
Azure's Cost Management export, the current and the previous month |
cost. |
support:, support: (it needs a support plan that includes Trusted Advisor) |
perf., perf. |
cloudwatch:, cloudwatch: |
aws. |
sqs:, sqs:, on the reader role's own queue, <RoleName>-ec2-lifecycle, alone, never on any other queue |
aws., for an account with CloudTrail attribution on |
cloudtrail:, one page of at most 50 events per configuration change, at most twice a second per account and region |
| Onboarding a management account, to read its members | organizations:, organizations: |
Granted ahead of a collector
These were granted when the template was written, for the EC2 collectors still to come: instance types, security group rules, network interfaces, route tables, gateways, endpoints, regions and zones. Each was reviewed as metadata-only. Nothing calls them yet. A collector that starts calling one moves it into its own list, as the NAT gateway collector did and the volume, VPC, subnet, security group and Elastic IP collectors did and the VPC peering collector did, and one that nobody ever calls is removed from the template.
ec2:DescribeInstanceTypes, ec2:DescribeSecurityGroupRules, ec2:DescribeNetworkInterfaces, ec2:DescribeRouteTables, ec2:DescribeInternetGateways, ec2:DescribeVpcEndpoints, ec2:DescribeTags, ec2:DescribeRegions, ec2:DescribeAvailabilityZones
The Google Cloud reader
deploy/gcp/reader-role.yaml, a custom role in each connected
Google Cloud project, bound to the project's reader service account. The
platform signs in by workload identity federation from its own AWS identity
and impersonates that account; no key file exists. The role grants exactly
the permissions below. No predefined viewer role would do: each reads content
somewhere, Datastore entities, a routine's body, a function's environment, a
service account's keys, or the project's shared instance metadata. Cloud
Functions are read from the Functions API's listing, masked, never its get.
Verification also
reads the reader account and the project (iam.serviceAccounts.get,
resourcemanager.projects.get).
| Module | Resource | Tier | Google permissions |
|---|---|---|---|
gcp. |
BigQuery datasets, per project; never a table, schema, routine or row | standard | bigquery. |
gcp. |
Service accounts, per project; never their keys | standard | iam. |
gcp. |
Firebase apps, per project; never their configuration | standard | firebase., firebase. |
gcp. |
Cloud Functions, per project, by their own name, whether or not they have run: state, labels and times; their triggers from the last two weeks' execution counts; never their environment, secrets, build or trigger configuration | standard | cloudfunctions., monitoring. |
gcp. |
Compute Engine instances, per region, zone by zone; never their metadata | standard | compute., compute. |
gcp. |
GKE clusters and their node pools, per region, at the region and each zone; never their credentials or node configuration | standard | compute., container. |
gcp. |
Cloud SQL instances, per project, their region a property | standard | cloudsql. |
gcp. |
Cloud Storage buckets, per project, their location a property; never an object or an access list | standard | storage. |
gcp. |
VPC networks, per project: mode, routing, the regions their subnets are in, peerings and their state | standard | compute. |
gcp. |
VPC subnets, per region: ranges, purpose, private access, flow logs | standard | compute. |
gcp. |
Cloud Run services, per region, second-generation Cloud Functions among them; never a container's environment, arguments or command, or a build's configuration | standard | run. |
gcp. |
Pub/Sub topics, per project; never a message or a message transform | standard | pubsub. |
gcp. |
Pub/Sub subscriptions, per project; never a message, a push endpoint or a message transform | standard | pubsub. |
gcp. |
Load balancers: a region's forwarding rules, each with the chain behind it (target proxy, URL map, backend services) and its backends' health; never a header value a backend service or a URL map carries | standard | compute., compute., compute., compute., compute., compute., compute. |
gcp. |
Load balancers: the project's global forwarding rules, each with the chain behind it and its backends' health; never a header value a backend service or a URL map carries | standard | compute., compute., compute., compute., compute., compute., compute., compute., compute. |
A load balancer's backend health is read with backendServices.getHealth, a
POST that reads and changes nothing; it needs the backend service's get
permission. It is the only POST the platform makes to Google Cloud, and the
platform refuses any other before it is sent.
Twelve permissions allow more than the product reads.
compute.instances.list returns each instance's metadata items (startup
scripts, SSH keys, any value an operator stored there);
container.clusters.list returns each cluster's master credentials and its
nodes' metadata; run.services.list returns each Cloud Run service's
containers' environment variables and their values, arguments and commands,
and its build's configuration; pubsub.topics.list returns each topic's
message transforms, which are JavaScript source;
pubsub.subscriptions.list returns the same transforms and each push
subscription's endpoint, a URL that may carry a credential;
compute.backendServices.list and compute.regionBackendServices.list, and
the get permissions beside them, return each backend service's custom
request and response headers, whose values may carry a credential, the
identifier of an AWS access key it signs with, and its IAP client and that
client's secret's hash; compute.urlMaps.list and
compute.regionUrlMaps.list return the header values a URL map's routes add
and match; and cloudfunctions.functions.list returns each function's build
and service configuration, its environment variables and their values and its
secret references among them, and its event trigger. None of them can be granted without those fields. Every request
the platform makes carries a partial-response field mask (fields=) that
names only the metadata it shows, so the content never crosses the wire, and
an answer that carries it anyway is refused whole and nothing of it is stored
(MILESTONE_1 §13.3). The mask is enforced by the platform's code and its
tests; the role itself would let the reader see the content.
Regions. Instances, clusters, subnets, Cloud Run services and regional
load balancers are read one region at a time, in the regions the project
uses; global load balancers once per project. A project registers with
none; region discovery finds them and adds them to the project's account, and
an admin may add more by hand. Discovery only adds: a region is removed by an
admin. A resource in a region the project had never used appears after the
next discovery, which runs daily and once when the project is verified,
rather than at the next collection. The subnets Google creates in every
region for an auto-mode network, such as default, are not counted as use: a
region holding only those is not added, and those subnets are listed on their
network rather than as rows of their own.
| Module | What it does | Cadence | Google permissions |
|---|---|---|---|
gcp. |
Finds the regions the project uses, from one names-only listing per regional service, and adds them to the project's regions; stores no resource | slow, per project | compute., compute., compute., compute., container., run. |
perf. |
Performance series of the project's measured resources, every region at once, from Cloud Monitoring's time series: values only, aggregated per resource, one read of a metric for up to a hundred resources | metrics (hourly), per project | monitoring. |
The Google Cloud billing export
Spend reads a Google Cloud billing account's detailed usage cost export, the
table gcp_billing_export_resource_v1_<billing account> that Google writes
into a BigQuery dataset the customer chooses. The export is enabled
in the Cloud console's Billing export page by someone holding Billing Account
Administrator or Billing Account Costs Manager; Google has no API for that
step. A dataset in the EU or US multi-region is backfilled from the start
of the previous month.
The read is not made under the reader's custom role, which stays metadata only. The project holding the export is connected like any other project, and its reader is bound two predefined roles beside that role:
| Role | Where | Why |
|---|---|---|
roles/ |
the project holding the export | to run the query (bigquery.) |
roles/ |
the export's dataset alone, never the project | to read the export's rows (bigquery.); the dataset should hold the export and nothing else |
The job cost.gcp_billing runs daily for every Google project. The project
holding an export runs one query a month for the current month and the two
before it, through BigQuery's jobs.query and jobs.getQueryResults. Each
query is capped by maximumBytesBilled (10 GiB unless the export's configuration
says otherwise), filtered on the export's partitions from the month's start to the
moment of the pull, and paged. A query over the cap is refused whole by
BigQuery, bills nothing and writes nothing; the run says to raise the cap.
A reader without the two roles is refused the same way, and the run names
them. The export's conversion rate is never read: each currency stays apart.
Corrections Google appends up to two months after their usage are read by a
later pull; older months keep what the last pull that reached them wrote.
An administrator configures each export on Spend's Settings page (or
POST /cost/gcp-billing-exports): the billing account, the table as
project.dataset.table, and the byte cap. Changing or removing one asks for
the password again; each write is in the audit log. The page shows each
export's last read — the latest run of cost.gcp_billing for the table's
project, with the reason when it was refused. Removing an export stops its
reads; what was read stays.
The Azure reader
deploy/azure/reader-role.json, a custom role in each connected
Azure subscription, assigned at the subscription to the app registration the
platform signs in as (deploy/azure/reader.bicep). The platform signs in by
outbound identity federation from its own AWS identity, or with a
certificate on a deployment off AWS; no client secret exists. The role grants
exactly the actions below, no wildcard and no data action. The built-in
Reader would not do: its */read reads a site's configuration and Key
Vault's secrets' names. Verification also reads the subscription
(Microsoft.Resources/subscriptions/read) and the app's own permissions
there (Microsoft.Authorization/permissions/read), and fails a role that
grants any action besides these.
Every Azure module runs once per subscription: an Azure resource's id names no location, so its region is a property the region filter reads, as a Cloud SQL instance's is. Each list is paged to its end, and a list that Azure throttles past its retries is incomplete, not empty.
| Module | Resource | Tier | Azure actions |
|---|---|---|---|
azure. |
Virtual machines and their power state; never their user data and no command | standard | Microsoft., Microsoft. |
azure. |
AKS clusters and their node pools; never their credentials | standard | Microsoft. |
azure. |
SQL databases, server by server, each server's master left out |
standard | Microsoft., Microsoft. |
azure. |
PostgreSQL flexible servers | standard | Microsoft. |
azure. |
Storage accounts and their availability; never their keys or a shared access signature | standard | Microsoft. |
azure. |
Virtual networks, their subnets and peerings | standard | Microsoft. |
azure. |
App Service apps, web apps and function apps; never their app settings or connection strings | standard | Microsoft. |
No action here returns content. A storage account's keys, a cluster's
credentials and a site's app settings come from other actions
(listKeys, listClusterAdminCredential, config/list), which the role
never grants and verification refuses to see granted. The sites' list
returns no settings; an answer that carried them would be refused whole and
nothing of it stored.
One job that is not a collector reads the subscription too, and the role holds its one action:
| Module | What it does | Cadence | Azure actions |
|---|---|---|---|
perf. |
Performance series of the subscription's measured resources — virtual machines, PostgreSQL flexible servers, SQL databases, storage accounts (with their blob service's count), App Service apps and AKS clusters — from Azure Monitor's platform metrics: numbers about each resource, one read per resource for every metric it has, a storage account's transactions read again split by response type; a resource deleted since the inventory read it is skipped; a statistic Azure refuses for one metric drops that series alone, and a resource Azure Monitor has no metric configuration for drops that resource for the run, each named on the run; nothing is kept from before a resource was made where Azure gives its creation time (virtual machines, SQL databases, storage accounts, PostgreSQL flexible servers), and for App Service apps and AKS clusters, which it gives none, nothing on a subscription's first read before the first sign of the resource's life | metrics (hourly), per subscription | Microsoft. |
A role that predates it fails verification's scope check, naming the
action, and its runs are reported, not failed, until the role is updated
from deploy/azure/reader-role.json.
The Azure export reader
Spend reads an Azure subscription's spend from the customer's own Cost
Management export (FOCUS, daily, to a storage container) — or from the
ActualCost and AmortizedCost exports of the same scope, both in that
container — never from the Cost Management APIs. deploy/azure/export-reader-role.json is
a second custom role, assigned to the same app on that one container
(deploy/azure/export-reader.bicep), never the storage account or wider. It
holds one data action and no action:
| Data action | What it reads |
|---|---|
Microsoft. |
the list of the export's blobs and their bodies |
An administrator configures each export on Spend's Settings page (or
POST /cost/azure-billing-exports): the subscription whose reader reads it,
the storage account, the container, the export's directory — and, for the
ActualCost and AmortizedCost pair, the second export's directory — and the
most one file may hold. Changing or removing one asks for the password again;
each write is in the audit log. The page shows each export's last read — the
latest run of cost.azure_billing for its subscription, with the reason when
it was refused. Removing an export stops its reads; what was read stays.
The platform asks for a Storage token for this read alone and sends it to the Blob service and nowhere else. It never writes or deletes a blob, never reads the account's keys and never asks for a user delegation key. The reader role above keeps no data action.
The report role
deploy/cloudformation/cost-report-role.yaml is deployed only in
an account that delivers a Cost & Usage Report, and only for the bucket and
prefix that account names. It is the one role that reads object contents: the
report's own files, which hold the account's billing line items.
| Statement | IAM action | Scope |
|---|---|---|
| List |
s3: |
the report bucket, and only keys under the report prefix |
| Read |
s3: |
objects under the report prefix, and nothing else in the bucket |
It has no bucket-wide read and no wildcard resource. A bucket encrypted with a
customer-managed KMS key also needs a decrypt grant on that key, which the
account adds to its own copy. Its trust policy is the reader role's: the
external id (sts:AssumeRole), the source identity (sts:SetSourceIdentity)
and the session tags (sts:TagSession).
The jobs that are not collectors
| Module | What it does | Cadence | Access |
|---|---|---|---|
cost., cost., cost. |
Cost Explorer ingestion and its backfill | daily, per account | the reader role |
cost.cur |
The Cost & Usage Report pull | daily, per account | the report role |
cost. |
Google Cloud's billing export pull, from BigQuery | daily, per Google project | the project's reader service account, with the two roles above |
cost. |
Trusted Advisor recommendations | daily, per account | the reader role |
cost. |
Kubernetes attribution | daily, per account | none of its own: the inventory and the report's line items |
cost., cost. |
Anomalies and budgets | daily, per tenant | none: the stored rollups |
cost. |
The month-end forecast, per tenant, team and account | daily, per tenant | none: the stored rollups |
cost. |
Measures every savings claim again and keeps a new version of its verdict when the answer changed | daily, per tenant | none: the stored per-resource cost rows |
perf. |
Performance series | metrics (hourly), per account and region | the reader role |
perf. |
Performance series of the resources with a global identity (Cloud |
metrics (hourly), per account | the reader role |
perf. |
Performance series of a Google Cloud project, from Cloud Monitoring | metrics (hourly), per project | the project's reader service account |
perf. |
Performance series of an Azure subscription, from Azure Monitor | metrics (hourly), per subscription | the reader app |
aws. |
The EC2 instances that start and stop between two reads: EC2's state-change events, from the queue the reader-role stack creates in each region | standard, per account and region | the reader role, on that queue alone |
aws. |
Who made a configuration change: Cloud |
on demand: when a change to a resource in an account with CloudTrail attribution on is 15 minutes old, per account and region, never on a schedule | the reader role |
Versions
| What | Version |
|---|---|
| PostgreSQL | 16 |
| Kubernetes | tested on 1.37 (kind v0.33.0, 2026-09-12). The manifests need 1.21 or later (policy/v1 disruption budgets). KEDA and External Secrets are optional components |
| The agent | 0.3.47, with OpenTofu 1.12.9 and the Library's first thirty-seven releases in its image. The platform admits agents from 0.2.0 (min); an agent runs the first release's templates from 0.2.1, the second's from 0.3.1, the third's from 0.3.2, the fourth's from 0.3.3, the fifth's from 0.3.4, the sixth's from 0.3.5, the seventh's from 0.3.6, the eighth's from 0.3.7, the ninth's from 0.3.8, the tenth's from 0.3.9, the eleventh's from 0.3.10, the twelfth's from 0.3.11, the thirteenth's from 0.3.12, the fourteenth's from 0.3.13, the fifteenth's from 0.3.14, the sixteenth's from 0.3.15, the seventeenth's from 0.3.16, the eighteenth's from 0.3.17, the nineteenth's from 0.3.18, the twentieth's from 0.3.19, the twenty-first's from 0.3.20, the twenty-second's from 0.3.21, the twenty-third's from 0.3.22, the twenty-fourth's from 0.3.24, the twenty-fifth's from 0.3.25, the twenty-sixth's from 0.3.26, the twenty-seventh's from 0.3.27, the twenty-eighth's from 0.3.36, the twenty-ninth's from 0.3.37, the thirtieth's from 0.3.38, the thirty-first's from 0.3.39, the thirty-second's from 0.3.40, the thirty-third's from 0.3.41, the thirty-fourth's from 0.3.42, the thirty-fifth's from 0.3.43, the thirty-sixth's from 0.3.44 and the thirty-seventh's from 0.3.45 |
| Built with | Go 1.26.10 (every module's go.mod) and Node 24.21.0 for the portal |
Generated from platform/docs/capabilities.md (06951eec68c3) by www/tools/docs; the value in brackets is a hash of the source's content, so a page changes only when its source does.