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 (BigQuery datasets, service accounts, Firebase apps and Cloud Functions, read through workload identity federation). Azure is planned.
- Deployment: self-hosted, with Docker Compose or the Kubernetes
kustomizations in
observability/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 (p7_81). Validation across separate customer accounts is in development.
Capabilities
| Module | Capability | Status |
|---|---|---|
| Estate | Inventory, derived health and the permanent status history | 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 | 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 | 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 | Google Cloud projects: BigQuery datasets, service accounts, Firebase apps and Cloud Functions, read through workload identity federation with a metadata-only custom role (p7_82) | 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 |
| 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 (p7_81) | available now |
| 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 | 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 | 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 | 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 | available now |
| Platform | The reviewer service itself, with a local model first | in development |
| Platform | The operator's manual, served to administrators inside the product | in development |
The reader role
observability/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,
CloudTrail events, CloudFront configurations or Lambda source, each of which
can hold a customer's data or credentials. 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.instance |
EC2 instances | standard | ec2:DescribeInstances, ec2:DescribeInstanceStatus |
k8s.eks.workload |
Kubernetes workloads on EKS | standard | eks:ListClusters, eks:DescribeCluster |
aws.eks.cluster |
EKS clusters | standard | eks:ListClusters, eks:DescribeCluster, eks:ListNodegroups, eks:DescribeNodegroup |
aws.rds.instance |
RDS instances | standard | rds:DescribeDBInstances |
aws.s3.bucket |
S3 buckets, per account | slow | s3:ListAllMyBuckets, s3:GetAccountPublicAccessBlock, s3:GetBucketTagging, s3:GetBucketVersioning, s3:GetEncryptionConfiguration, s3:GetBucketPublicAccessBlock, s3:GetBucketPolicyStatus |
aws.dynamodb.table |
DynamoDB tables | standard | dynamodb:ListTables, dynamodb:DescribeTable, dynamodb:DescribeContinuousBackups, dynamodb:ListTagsOfResource |
aws.elb.loadbalancer |
Load balancers and their targets | standard | elasticloadbalancing:DescribeLoadBalancers, elasticloadbalancing:DescribeTargetGroups, elasticloadbalancing:DescribeTargetHealth, elasticloadbalancing:DescribeTags |
aws.sqs.queue |
SQS queues | standard | sqs:ListQueues, sqs:GetQueueAttributes, sqs:ListQueueTags |
aws.sns.topic |
SNS topics | standard | sns:ListTopics, sns:GetTopicAttributes, sns:ListSubscriptionsByTopic, sns:ListTagsForResource |
aws.apigateway.api |
API Gateway APIs | standard | apigateway:GET |
aws.backup.vault |
AWS Backup vaults | standard | backup:ListBackupVaults, backup:ListTags |
aws.acm.certificate |
ACM certificates | standard | acm:ListCertificates, acm:DescribeCertificate, acm:ListTagsForCertificate |
aws.ec2.natgateway |
NAT gateways | standard | ec2:DescribeNatGateways |
aws.cloudfront.distribution |
CloudFront distributions with traffic in the last two weeks, per account, by id; their status unknown | standard | cloudwatch:ListMetrics |
aws.kinesis.stream |
Kinesis data streams | standard | kinesis:ListStreams, kinesis:DescribeStreamSummary, kinesis:ListTagsForStream |
aws.lambda.function |
Lambda functions invoked in the last two weeks, by name; their status unknown | standard | cloudwatch:ListMetrics |
aws.elasticache.cluster |
ElastiCache clusters | standard | elasticache:DescribeCacheClusters, elasticache:ListTagsForResource |
aws.opensearch.domain |
OpenSearch Service domains, by name and engine; their status unknown | standard | es:ListDomainNames, es:ListTags |
aws.glue.job |
Glue jobs, by name; their status unknown | standard | glue:ListJobs, glue:GetTags |
aws.athena.workgroup |
Athena workgroups | standard | athena:ListWorkGroups, athena:ListTagsForResource |
aws.ec2.volume |
EBS volumes and their status checks | standard | ec2:DescribeVolumes, ec2:DescribeVolumeStatus |
aws.ec2.vpc |
VPCs | standard | ec2:DescribeVpcs |
aws.ec2.subnet |
Subnets | standard | ec2:DescribeSubnets |
aws.ec2.securitygroup |
Security groups, their rules counted | standard | ec2:DescribeSecurityGroups |
aws.ec2.elastic-ip |
Elastic IP addresses | standard | ec2:DescribeAddresses |
Called by the other jobs and by onboarding
| Caller | IAM actions |
|---|---|
cost.ce_aggregate, cost.ce_resource, cost.ce_backfill |
ce:GetCostAndUsage, ce:GetCostAndUsageWithResources |
cost.trusted_advisor |
support:DescribeTrustedAdvisorChecks, support:DescribeTrustedAdvisorCheckResult (it needs a support plan that includes Trusted Advisor) |
perf.cloudwatch, perf.cloudwatch.global |
cloudwatch:GetMetricData, cloudwatch:ListMetrics |
| Onboarding a management account, to read its members | organizations:ListAccounts, organizations:DescribeOrganization |
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, peering, 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 (p7_80), and one that nobody ever calls is removed from the template.
ec2:DescribeInstanceTypes, ec2:DescribeSecurityGroupRules, ec2:DescribeNetworkInterfaces, ec2:DescribeRouteTables, ec2:DescribeInternetGateways, ec2:DescribeVpcEndpoints, ec2:DescribeVpcPeeringConnections, ec2:DescribeTags, ec2:DescribeRegions, ec2:DescribeAvailabilityZones
The Google Cloud reader
observability/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, or
a service account's keys. Cloud Functions are found from their metrics, as
Lambda's are. Verification also reads the reader account and the project
(iam.serviceAccounts.get, resourcemanager.projects.get).
| Module | Resource | Tier | Google permissions |
|---|---|---|---|
gcp.bigquery.dataset |
BigQuery datasets, per project; never a table, schema, routine or row | standard | bigquery.datasets.get |
gcp.iam.serviceaccount |
Service accounts, per project; never their keys | standard | iam.serviceAccounts.list |
gcp.firebase.app |
Firebase apps, per project; never their configuration | standard | firebase.clients.list, firebase.projects.get |
gcp.functions.function |
Cloud Functions that ran in the last two weeks, per project, by name; their status unknown | standard | monitoring.timeSeries.list |
The report role
observability/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 |
|---|---|---|
| ListReportPrefix | s3:ListBucket |
the report bucket, and only keys under the report prefix |
| ReadReportObjects | s3:GetObject |
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.ce_aggregate, cost.ce_resource, cost.ce_backfill |
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.trusted_advisor |
Trusted Advisor recommendations | daily, per account | the reader role |
cost.kubernetes |
Kubernetes attribution | daily, per account | none of its own: the inventory and the report's line items |
cost.detect_anomalies, cost.evaluate_budgets |
Anomalies and budgets | daily, per tenant | none: the stored rollups |
cost.forecast |
The month-end forecast, per tenant, team and account (p3_21) | daily, per tenant | none: the stored rollups |
perf.cloudwatch |
Performance series | standard, per account and region | the reader role |
perf.cloudwatch.global |
Performance series of the resources with a global identity (CloudFront), read in us-east-1 | standard, per account | 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.2.0, with OpenTofu 1.12.6 in its image. The platform admits agents from 0.2.0 (minAgentVersion) |
| Built with | Go 1.26.8 (every module's go.mod) and Node 20.19.5 for the portal |