Notes / How the platform works
Notes from
the platform.
One topic per note: what each part of the platform does, why it is built that way, and what it costs. Open a note to read it here, step through the rest, or open its own page.
Terraform to Flux through one ConfigMap
The GitOps Bridge pattern for Flux: Terraform publishes the AWS facts once, every manifest substitutes them, and no manifest names an account, a region or an ARN.
- Terraform
- Flux
- GitOps
- AWS
Cilium from the first node, and why not the VPC CNI
Why the platform creates EKS without the VPC CNI and kube-proxy, what Cilium in ENI mode gives that the default does not, and what it costs.
- Kubernetes
- Cilium
- AWS
One way out to the internet, three fences around it
How the Secure package makes a cluster unable to reach the internet except through one allowlisting gateway, and why one control is never enough.
- AWS
- Kubernetes
- Cilium
- Envoy
- Security
Capabilities: a network policy model that does not drift
A default-deny network policy model where workloads declare dependencies by label, destinations admit the same label, and a narrowly privileged pipeline applies it all.
- Kubernetes
- Cilium
- GitOps
- Security
Testing admission policies both ways
Kyverno CEL policies with fixtures that must pass and fixtures that must fail, reported in development and enforced in production from the same files.
- Kubernetes
- Kyverno
- Security
Guardrails an AWS account inherits on day one
Service and resource control policies attached once per OU, tested offline, and changed only after replaying real CloudTrail history against them.
- AWS
- Security
- Terraform
Atlantis, and a policy a pull request cannot weaken
Plans on every pull request, conftest checks on the plan itself, applies only of the reviewed plan, and policies baked into the image that runs them.
- CI/CD
- Atlantis
- Terraform
- Security
Three products from one repository with Terramate
One variable selects the package, Terramate composes the stacks, and a renderer gives each client a repository that contains only what they bought.
- Terraform
- Terramate
Upgrades a client receives as a pull request
How a turnkey platform keeps moving after handover: a baseline branch in every client repository, and Git merging new releases with the client's own changes.
- GitOps
- Terraform
- CI/CD
One IAM role for every service, and no neighbour's secrets
EKS Pod Identity session tags turn one shared role into per-service permissions: a pod reads only the secrets, tables and buckets under its own namespace and service account.
- AWS
- Kubernetes
- Security
Nodes without internet: Bottlerocket and ECR pull-through cache
How nodes start, join and pull images when their subnets have no route out, and what that removes from the attack surface.
- AWS
- Kubernetes
- Security
An SRE agent inside the cluster that never acts alone
oncall-kit for behaviour, Claude Tag for the conversation, self-hosted runners in the cell for the data: how the Autonomous package builds its agents.
- AI agents
- Observability
- Kubernetes
- Security
No note on this topic yet.
Note 01 / 12
Terraform to Flux through one ConfigMap
The GitOps Bridge pattern for Flux: Terraform publishes the AWS facts once, every manifest substitutes them, and no manifest names an account, a region or an ARN.
- Terraform
- Flux
- GitOps
- AWS
The gap between the two tools
Add-ons on EKS need facts only the infrastructure code knows: the Karpenter interruption queue, the role ARN for the load balancer controller, the VPC ID, the KMS key for node volumes. Terraform creates those things; Flux installs the charts that need them. Copying the values into Git by hand drifts on the next apply. Letting Terraform install the charts with the Helm provider makes Terraform own state that Flux then changes under it.
The GitOps Bridge project names the pattern that closes this gap: the infrastructure tool creates the cloud resources and stores their metadata where the GitOps engine can read it; the GitOps engine installs everything and passes the metadata to the charts. Its reference implementation is for Argo CD: Terraform writes the cluster secret, add-on switches become labels, values become annotations, and ApplicationSets select clusters by label and template values from the annotations. The Flux side of the project is still in progress, so this is how the platform does it for Flux.
One ConfigMap, written once
Terraform writes a single ConfigMap, cell-metadata, into flux-system. It holds identifiers only, never a secret value:
resource "kubernetes_config_map_v1" "cell_metadata" {
metadata {
name = "cell-metadata"
namespace = "flux-system"
}
data = {
cell_name = var.name
aws_region = local.region
cluster_name = module.eks.cluster_name
karpenter_interruption_queue = module.karpenter.queue_name
karpenter_node_role = module.karpenter.node_iam_role_name
app_role_arn = aws_iam_role.app_abac.arn
admission_action = var.environment == "dev" ? "Audit" : "Deny"
}
}Every Flux Kustomization substitutes from it after building, so a manifest simply writes ${cluster_name}:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: controllers
namespace: flux-system
spec:
path: ./infrastructure/controllers
postBuild:
substituteFrom:
- { kind: ConfigMap, name: cell-metadata }What differs from the Argo CD bridge
- Which add-ons a cell runs is decided in Git, not by labels Terraform writes. Each cell's directory lists its layers and optional components. A reviewer sees the full composition in one pull request; Terraform never decides what is installed.
- Strict substitution. Flux fails a reconciliation when a manifest asks for a variable the ConfigMap does not have, so a missing value is a loud error, not an empty string in a chart.
- Terraform installs only what must exist before Flux: the CNI, the egress gateway when the cell has one, and Flux itself. Everything else is Flux's.
- Identity before workload. Terraform creates a workload's role and Pod Identity association before Flux deploys the workload, so a pod never starts without its permissions and Flux never needs IAM rights.
Rules that keep it honest
- Identifiers only. A database endpoint is fine; its password is not. Secrets reach the cluster through External Secrets or Pod Identity.
- The ConfigMap carries a schema number. Adding or renaming a key is a change on both sides, reviewed together.
- CI renders every layer with a sample ConfigMap in strict mode, so a typo in
${variable}fails the pull request, not the cluster.
The result is one GitOps tree for every cell. A new region is a new Terraform stack and a new directory that lists what the cell runs; no manifest is copied.
Note 02 / 12
Cilium from the first node, and why not the VPC CNI
Why the platform creates EKS without the VPC CNI and kube-proxy, what Cilium in ENI mode gives that the default does not, and what it costs.
- Kubernetes
- Cilium
- AWS
The default, and why it is not enough
An EKS cluster comes with the Amazon VPC CNI for pod networking and kube-proxy for Services. They work, and for many clusters they are the right choice. The platform replaces both with Cilium, for reasons that are about security and operations rather than taste.
Why Cilium
- Policy by identity, not by IP. Cilium enforces network policy on workload identity derived from labels, in the kernel with eBPF. Pod IPs change constantly; identities do not. The platform's default-deny model with declared capabilities depends on this.
- FQDN policy.
toFQDNslets a policy say "this workload may reach api.example.com on 443" and have Cilium's DNS proxy enforce it. The VPC CNI's network policy support has no equivalent; the egress gateway's second fence is built on it. - Seeing what was dropped. Hubble records every flow with its verdict. "Why can't A reach B" becomes a log query instead of a guess, and dropped flows become an alert.
- kube-proxy replacement. Service load balancing in eBPF with Maglev hashing instead of iptables chains that grow with every Service, and node processes can reach ClusterIPs, which the platform uses so nodes pull images through the egress gateway.
- L7 only where needed. When a route needs retries or gRPC-aware balancing, Cilium sends that traffic through a node-local Envoy; everything else stays in the kernel. No sidecar in every pod.
Why from the first node
The common path is to create the cluster with the VPC CNI, install Cilium later, and remove the old CNI with a script. That means two applies, a period where nodes run two datapaths, and a cleanup step that has to be right on every cluster.
The platform instead creates EKS with no self-managed add-ons, installs Cilium with Terraform right after the control plane, and only then creates node groups. Nodes carry a node.cilium.io/agent-not-ready taint until Cilium is running on them, and CoreDNS tolerates it. One apply builds a working cluster.
resource "helm_release" "cilium" {
chart = "cilium"
wait = false # nodes join after this release
values = [yamlencode({
ipam = { mode = "eni" }
eni = { enabled = true, awsEnablePrefixDelegation = true }
routingMode = "native"
kubeProxyReplacement = true
})]
}
module "node_group_system" { depends_on = [helm_release.cilium] }Why ENI mode
In ENI mode Cilium gives each pod an address from its node's subnet, as the VPC CNI does. That keeps pods routable in the VPC without overlays, and it is what makes subnet tiers mean something for pods: a pod on a private-tier node is in the private tier and has no route to the internet; a pod on an egress-pool node leaves through that pool's NAT and fixed IP.
What it costs
- You own the CNI. Cilium upgrades are platform changes, planned and rolled cell by cell, not an AWS-managed add-on version bump.
- Support boundary. AWS supports the VPC CNI; for Cilium you rely on the project, a commercial distribution, or your own team.
- IP planning still matters. ENI mode uses VPC addresses; prefix delegation makes it efficient, but subnets must be sized for pods, not only nodes.
For a platform whose security model is built on identity-aware policy, FQDN egress and flow visibility, those costs are worth paying. For a single small cluster with no network policy, the default is fine.
Note 03 / 12
One way out to the internet, three fences around it
How the Secure package makes a cluster unable to reach the internet except through one allowlisting gateway, and why one control is never enough.
- AWS
- Kubernetes
- Cilium
- Envoy
- Security
The problem
Most clusters let any pod open a connection to anywhere. A compromised dependency calling home, a cryptominer, data leaving through an innocent-looking HTTPS request: none of them need more than that. Network policy alone does not fix it, because it is one mistake away from open: a broad rule added in a hurry, a selector that matches nothing, an agent left in audit mode.
Three independent fences
- Routing. Node and pod subnets have no default route. Only a dedicated egress tier routes to NAT, and only the egress gateway runs there. A pod that ignores every policy still has nowhere to send internet traffic.
- CNI policy. The gateway pods may reach only the allowlisted hostnames, enforced by Cilium's DNS proxy with
toFQDNs. Application pods may reach the gateway only if they carry the capability label. - L7 proxy. The gateway is an Envoy CONNECT proxy. It forwards only allowlisted
host:portpairs and answers everything else with a 403 and a JSON log line.
A security group lets only the egress tier send HTTPS out of the VPC: a fourth, coarse fence. AWS APIs never touch the gateway; they are reached through VPC endpoints.
virtual_hosts:
- name: allowed
domains: ["github.com:443", "*.githubusercontent.com:443", "api.stripe.com:443"]
routes:
- match: { connect_matcher: {} }
route: { cluster: dynamic_forward_proxy, upgrade_configs: [{ upgrade_type: CONNECT, connect_config: {} }] }
- name: denied
domains: ["*"]
routes:
- match: { connect_matcher: {} }
direct_response: { status: 403 }The details that matter
- Wildcards only for a vendor's own zone.
*.github.iowould let anything hosted on GitHub Pages through;*.s3.amazonaws.comis an exfiltration channel, not an allowlist entry. - Nodes pull container images through the same gateway, by a fixed ClusterIP, because they do not use cluster DNS. Even image pulls follow the policy.
- The gateway is installed by Terraform, before Flux, because Flux reaches GitHub through it.
- Workloads that must call hosts nobody can list (webhooks to customer URLs) get an egress pool with its own fixed IP and a named exception, instead of a wildcard on the gateway.
What changes
Leaving the network becomes a decision someone reviews. A new dependency is a one-line allowlist change in a pull request; an unexpected destination shows up as a 403 and a dropped flow instead of a successful connection. And the allowlist is, for free, the documentation of everything the platform talks to.
Note 04 / 12
Capabilities: a network policy model that does not drift
A default-deny network policy model where workloads declare dependencies by label, destinations admit the same label, and a narrowly privileged pipeline applies it all.
- Kubernetes
- Cilium
- GitOps
- Security
Why hand-written policies drift
A caller's egress rule and the destination's ingress rule are usually written in different places by different people. A rule gets copied with more than it needs. A "temporary" broad rule stays because nobody can prove what removing it would break.
Capabilities
Every tenant namespace is deny-by-default. A shared dependency is a capability: a reusable policy component the namespace lists, and a label the workload carries. The destination admits callers by the same label, so the two sides cannot drift apart.
# destination side: admit any caller that carries the capability, from any namespace
ingress:
- fromEndpoints:
- matchLabels: { netpol.cell.io/svc-payments: "true" }
matchExpressions:
- { key: k8s:io.kubernetes.pod.namespace, operator: Exists }
toPorts: [{ ports: [{ port: "8080", protocol: TCP }] }]The matchExpressions line is not decoration: without it, a fromEndpoints selector silently means "only this namespace".
Where the line is
The test for "is this a capability" is: if a second workload carried this label, would that be correct? Shared services and infrastructure pass it. A destination only one workload uses, such as its own bucket or database, does not: it lives as a private policy in that namespace, so a label can never grant access to someone else's data.
Guardrails on the guardrails
- Policies are applied by a Flux service account that can manage Cilium policies and nothing else.
- CI rejects
world,clusteror0.0.0.0/0in allow rules, FQDN patterns that cover a whole provider, CIDR rules without ports, and the cross-namespace selector that silently means "this namespace only". - Dropped flows are exported and alerted on, so a missing capability becomes a ticket instead of a timeout someone debugs for a day.
- Broad legacy rules are removed only after real traffic shows the replacement covers every flow: parity first, removal second.
Who may talk to whom becomes readable in one directory and reviewable in one pull request.
Note 05 / 12
Testing admission policies both ways
Kyverno CEL policies with fixtures that must pass and fixtures that must fail, reported in development and enforced in production from the same files.
- Kubernetes
- Kyverno
- Security
The failure nobody sees
An admission policy that blocks nothing looks exactly like one that works. A verb that does not exist, a check on a field an attacker controls, a match that never fires: all pass review, all are silent, and all are discovered during an incident.
Tested both ways
Every policy ships with fixtures that must be admitted and fixtures that must be rejected by that policy, by name. The test fails if a good fixture is rejected or a bad one gets through.
results:
- { policy: require-approved-registries, resources: [good-app], kind: Deployment, result: pass }
- { policy: require-approved-registries, resources: [bad-registry], kind: Deployment, result: fail }
- { policy: restrict-load-balancers, resources: [bad-public-lb, bad-misspelled-ranges], kind: Service, result: fail }What the policies cover
Pod Security Admission handles privileged pods, host access and capabilities natively. Kyverno, in CEL, adds what it does not: images only from the cell registry and signed by the release workflow, pinned versions, requests and memory limits, probes, read-only root filesystems, no default service account, internal load balancers unless exposure is approved, cluster-admin bound only by GitOps, and a namespace syncing only its own secrets.
One policy rejects a misspelled load balancer annotation. The controller ignores the misspelling and opens the load balancer to the internet while the manifest looks restricted. That kind of rule comes from incidents, and it is exactly the kind a test must prove.
Same files, two modes
The action comes from the cell: Audit in development, Deny in staging and production. Teams see violations in their dev cell before production rejects them. Exceptions exist, but each names an owner, a reason and an expiry date, and exempts a workload, never a namespace.
Note 06 / 12
Guardrails an AWS account inherits on day one
Service and resource control policies attached once per OU, tested offline, and changed only after replaying real CloudTrail history against them.
- AWS
- Security
- Terraform
How guardrails rot
Guardrails grow account by account: the same rule copied with a different list of ARNs, accounts at the five-policy quota, new accounts with none at all. Nobody can say what an account actually has without reading every policy on its path.
One rule, one place
Each rule lives once, at the highest level of the organization where it is true:
- Root: the audit trail cannot be stopped, the root user can do nothing, nobody leaves the organization, and the tags that grant privilege through ABAC can only be set by security admins.
- Workloads OU: every role has a permissions boundary, regions are locked, IMDSv2 and encryption are required, interactive host access goes through the access proxy.
- Production OU: only CI publishes images, only autoscalers start instances, only code changes cluster access and network edges.
- A resource control policy keeps S3, Secrets Manager and KMS closed to principals outside the organization and to plain HTTP, whatever a resource policy says.
SCPs have no audit mode
A broken SCP is found by an outage. So every change passes three gates. Offline tests check the quota, that every statement is a Deny, and that the break-glass role keeps a way in. A replay of ninety days of CloudTrail lists every principal the candidate would have denied. And new policies are attached next to the old ones for 48 hours before anything is removed, OU by OU, production last; policies combine as a union of denies, so this never opens a gap.
run "guardrails_only_deny" {
command = plan
assert {
condition = alltrue(flatten([
for p in aws_organizations_policy.scp : [for s in jsondecode(p.content).Statement : s.Effect == "Deny"]
]))
error_message = "An SCP contains an Allow statement; guardrails only take permissions away."
}
}The replay is the gate that pays for itself: it catches the change that would have stopped node autoscaling in production because one role name did not match a new pattern.
Note 07 / 12
Atlantis, and a policy a pull request cannot weaken
Plans on every pull request, conftest checks on the plan itself, applies only of the reviewed plan, and policies baked into the image that runs them.
- CI/CD
- Atlantis
- Terraform
- Security
What CI applies were missing
Moving applies from laptops to CI removes credentials from laptops. It does not guarantee that the plan a reviewer approved is the plan that runs, and security rules live only in reviewers' heads.
How the platform applies
- Atlantis plans every changed stack and posts the plan to the pull request. Projects are generated from the Terramate stack list, so the order follows the stacks' dependencies.
- Each plan is checked with conftest before it may be applied: protected resources cannot be destroyed without an earlier, reviewed
allow-destroytag; open ingress, wildcard IAM, unencrypted storage and public buckets fail. - Apply requires an approving review, a mergeable pull request and a branch up to date with main, and applies exactly the saved plan.
- Atlantis has its own workload identity that may only assume a plan role and an apply role in each account. No keys.
deny contains msg if {
some rc in input.resource_changes
rc.type in protected
"delete" in rc.change.actions
object.get(object.get(rc.change.before, "tags", {}), "allow-destroy", "false") != "true"
msg := sprintf("%s would be destroyed; tag it allow-destroy=true in a reviewed change first", [rc.address])
}The policies live in the image
If the policies were read from the repository, a pull request could change the policy that checks it. They are baked into the Atlantis image instead, built and signed by CI; a policy change is a new image, reviewed like any other change. Overrides exist, but only the policy owners can approve one, and the approval is recorded on the pull request.
Note 08 / 12
Three products from one repository with Terramate
One variable selects the package, Terramate composes the stacks, and a renderer gives each client a repository that contains only what they bought.
- Terraform
- Terramate
The problem with feature flags
The platform is sold as three packages. Clients receive the source. Flags that switch off what a client did not buy still ship that code to them: noise to maintain, capabilities they do not have, and the higher packages given away.
One variable composes the stacks
Capabilities live in separate modules per package. A global, tier, decides which modules each stack is generated from; Terramate writes the Terraform and it is committed, so reviewers read real code.
generate_hcl "egress.tf" {
condition = tm_contains(terramate.stack.tags, "cell") && global.tier != "launch"
content {
module "egress" {
source = "${terramate.stack.path.to_root}/modules/cell-egress"
cluster_ip = module.cell.egress.cluster_ip
}
}
}Rendering a client copy
A manifest maps paths to the lowest package that includes them; lines that differ inside shared files carry markers. The renderer copies only what the client's package includes, cuts the marked blocks, sets the tier, regenerates and validates. The result contains no trace of the higher packages.
egress_allowlist_hosts = [
"api.stripe.com",
# >>> tier:autonomous
"api.anthropic.com",
# <<< tier:autonomous
]CI renders all three packages from the baseline on every pull request and runs the full checks on each, so a change that leaks a higher package into a lower one fails before it merges.
Note 09 / 12
Upgrades a client receives as a pull request
How a turnkey platform keeps moving after handover: a baseline branch in every client repository, and Git merging new releases with the client's own changes.
- GitOps
- Terraform
- CI/CD
Turnkey is not frozen
A client owns their copy of the platform. Kubernetes, add-ons and providers release every month and security fixes do not wait. Without a delivery channel, a platform starts decaying the day it is handed over.
The vendor-branch pattern
Each client repository keeps a baseline branch that only ever contains rendered baseline code, never client edits. .platform.json records the baseline version and package.
- A new baseline version is rendered for the client's package onto
baseline. - A branch from
mainmergesbaseline. - Git combines the new baseline with the client's own changes; conflicts appear only where the client edited baseline files.
- The client reviews and merges a normal pull request; plans and policy checks run as for any change.
Moving from one package to the next is the same operation with a different package: the new modules arrive as a pull request, next to the client's own work.
Who runs it
The mechanism is technical, not contractual. A client can run the upgrade themselves from release notes, or have it prepared, reviewed and applied under a support plan. Either way the change is a diff they can read.
Note 10 / 12
One IAM role for every service, and no neighbour's secrets
EKS Pod Identity session tags turn one shared role into per-service permissions: a pod reads only the secrets, tables and buckets under its own namespace and service account.
- AWS
- Kubernetes
- Security
The IAM sprawl problem
A role per service means a Terraform change for every new service, dozens of near-identical policies, and the steady temptation to reuse one role for "just this other service".
Session tags do the scoping
EKS Pod Identity tags every session with the pod's namespace and service account. One shared application role can then use those tags in resource paths:
statement {
sid = "OwnSecretsOnly"
actions = ["secretsmanager:GetSecretValue"]
resources = [
"arn:aws:secretsmanager:${region}:${account}:secret:${cell}/services/$${aws:PrincipalTag/kubernetes-namespace}/$${aws:PrincipalTag/kubernetes-service-account}/*"
]
}A pod in payments running as api reads <cell>/services/payments/api/* and nothing else. Tables and buckets follow the same prefix rule. A label change cannot widen it: the tags come from the platform, not from the pod.
Where it stops
- A workload that needs something outside its prefix gets its own role, declared in code with the reason.
- External Secrets syncing into a namespace is limited to that namespace's prefix by an admission policy, so the cluster-wide secret store cannot become a back door.
- Every role, shared or not, carries a permissions boundary the organization requires.
Note 11 / 12
Nodes without internet: Bottlerocket and ECR pull-through cache
How nodes start, join and pull images when their subnets have no route out, and what that removes from the attack surface.
- AWS
- Kubernetes
- Security
Why bother
If a node can reach the internet, so can anything that compromises it. In the Secure package, node and pod subnets have no default route at all. That only works if nodes can still boot, join the cluster and pull images.
What makes it work
- VPC endpoints for every AWS API a node needs: ECR, STS, EKS, EC2, SSM, KMS, logs. S3 and DynamoDB through gateway endpoints.
- ECR pull-through cache for the platform's own images: quay, registry.k8s.io, public ECR, GitHub and Docker Hub are mirrored into the account's ECR on first pull, scanned continuously, and expired when unused. The CNI and Flux images come from there.
- The egress gateway as the node's proxy for anything else. Bottlerocket's
https-proxysetting sends containerd's pulls through the gateway by a fixed ClusterIP, so node pulls obey the same allowlist as everything else.
[settings.host-containers.admin]
enabled = false
[settings.kernel]
lockdown = "integrity"
[settings.network]
https-proxy = "http://172.20.0.50:3128"
no-proxy = [".svc", ".cluster.local", "169.254.169.254", ".amazonaws.com"]Bottlerocket, hardened
A minimal OS with an immutable root filesystem. The admin and control host containers are off: access is through the cluster, not the host. Kernel lockdown is on, IMDSv2 is required with a hop limit of one, so a pod cannot reach the node's instance credentials, and every volume is encrypted with the cell's key.
Note 12 / 12
An SRE agent inside the cluster that never acts alone
oncall-kit for behaviour, Claude Tag for the conversation, self-hosted runners in the cell for the data: how the Autonomous package builds its agents.
- AI agents
- Observability
- Kubernetes
- Security
What the agent is for
Most alerts need the same first hour of work: which service, since when, what changed, what the logs say. An agent can do that hour. The risk is not that it is slow; it is that it acts with too much power on too little evidence.
Built on proven pieces
- Behaviour: oncall-kit. Its setup mines the team's incident history into triage playbooks, keeps the agent read-only, ends every phase at a human gate, and requires a blind replay eval and a shadow period before anyone relies on it.
- Conversation: Claude Tag in the team's Slack on-call and incident channels.
- Execution: self-hosted runners inside the cell. Hosted sandboxes cannot reach private endpoints, so sessions run next to the data. An orchestrator holds the environment key and starts one Kubernetes Job per session with a single-use work order; the key never reaches a pod that runs a session.
# spawn-runner hook: one Job per order, named from the order ID so redelivery creates nothing new
name="runner-$(printf '%s' "$CLAUDE_RUNNER_ORDER_ID" | tr 'A-Z_' 'a-z-' | cut -c1-50)"
kubectl -n agents get job "$name" >/dev/null 2>&1 && exit 0
# ... create the Job, store the work order in a Secret owned by it, exit without waitingGuarantees that do not depend on the model
- Read-only RBAC with no Secrets, no exec, no write verbs.
- Managed settings in the image deny write commands; the image carries read-only tools only.
- Network: the model API, GitHub and the in-cluster observability endpoints, nothing else.
- Fixes are proposals: a message in the thread or a pull request. People decide and deploy.
Text the agent reads from logs, alerts and tickets is data, never instructions; that rule is in its prompt, and the credentials make sure it does not matter if the prompt is ignored.
Start a conversation
Bring your infrastructure
into focus.
Tell me what you are building, operating, or trying to improve.