ADR 0002: v0 scope — Queryable state
- Status: Accepted
- Date: 2026-05-12
- Decider(s): @davesade (David Kubec)
- Supersedes / Relates to: ADR 0001
Context
Kilolock's eventual value lies in graph-scoped plans and parallel applies, but those depend on a working data model, a stable on-disk schema, and a reliable migration path from existing .tfstate files. Building the plan engine first means building it on top of an unproven schema, which guarantees rework.
A separate problem: shipping nothing for a year while a full graph-scoped engine matures is a known failure mode for ambitious infrastructure projects. There needs to be a usable v0 that justifies the schema work on its own.
Decision
v0 of Kilolock is scoped to queryable state. The goal: take an existing .tfstate file, normalize it into the Postgres schema, and let users query the result with SQL. No plan or apply changes; users keep running terraform / tofu exactly as today.
This is intentionally narrow. It is also intentionally useful: many of the real-world pains around large states (inventory, drift detection, compliance reporting, cross-state queries) are addressable with read-only queries alone.
Goals (in scope for v0)
- HTTP backend protocol server. Implement the documented Terraform
httpbackend so users can point theirbackend "http"block atKilolock and have state stored in Postgres.
- Round-trip fidelity. A
.tfstateimported into Kilolock and thenexported back must be byte-equivalent (modulo whitespace and key
ordering) to the original. This is the non-lock-in guarantee.
- Normalized schema. Resources, attributes, dependencies, outputs, and
modules are stored as proper rows, not as opaque JSON blobs. Attributes
stay in JSONB for shape flexibility, but the structural edges of the
graph live in real relational tables.
- Query CLI.
kl query "SELECT ..."runs read-only SQL againstthe graph and returns formatted results (table, JSON, CSV).
- Standard inventory queries documented. A handful of canonical example
queries shipped in
docs/queries/: "find resources by type across allstates," "show transitive dependents of resource X," "find resources
matching attribute predicate."
- Single-command local setup.
docker compose upbrings Postgres up;kl migrateapplies the schema;kl import path.tfstateloads a state. Total time: under 60 seconds on a developer laptop.
Non-goals (explicitly deferred)
- Plan or apply orchestration. No subgraph plans, no scoped refresh, no
apply orchestration. v0 is read-mostly with respect to the cloud — it
stores and serves state, it does not interact with providers.
- HCL parsing. v0 does not look at HCL at all. It receives a
.tfstateblob (via import or via the HTTP backend
POST) and normalizes it. - Authentication / authorization. v0 ships with a single trusted-network
deployment model. RBAC and per-resource permissions belong to a later ADR.
- UI. No web UI in v0. SQL via the CLI is the user-facing surface.
- Multi-tenancy. One logical Kilolock instance per organization. Tenant
isolation is a much later concern.
- Drift detection. Inferring drift requires querying providers, which is
v1 territory (provider-protocol-native engine).
- High availability. v0 is single-instance. Postgres is the durability
boundary; the Kilolock process is restartable and stateless.
Acceptance criteria
v0 ships when all of the following are true:
- **
kld** starts an HTTP server that passes the Terraformhttpbackend contract:GET,POST,LOCK,UNLOCK,DELETE. - **
kl import <file.tfstate>** loads any valid Terraform v4 statefile produced by Terraform 1.x or OpenTofu 1.x, with no data loss.
- **
kl export <state-name> -o <file.tfstate>** produces a statefile that, when used by
terraform plan, results in no diff againstthe original state file.
- **
kl query "SELECT ..."** executes against the graph andreturns results as table, JSON, or CSV (via
--format). - Round-trip property test. A test corpus of at least 10 real-world
.tfstatefiles (sizes ranging from ~50KB to ~50MB) round-trips throughimport/export with verified byte-equivalence.
- End-to-end demo. A repeatable demo: spin up Postgres via
docker-compose, configure a sample Terraform project to use Kilolock
as backend, run
terraform apply, observe rows in Postgres, runsample SQL queries against the result.
Out of scope for v0 — explicitly listed to set expectations
- Migration support for the
s3,gcs,azurerm,consul,pg, andTerraform Cloud backends. v0 supports the
httpbackend only. Users withstate in other backends are expected to
terraform state pull > x.tfstateand
kl import x.tfstate. - Web hooks or notifications on state change.
- Sensitive attribute handling beyond what Terraform already does in the
source state file. Kilolock stores what Terraform writes; treating
sensitive values specially is a later ADR.
- Performance optimization beyond "fast enough on a developer laptop."
Benchmarks and tuning are a v1 concern.
Consequences
Positive.
- A working v0 ships in weeks to months of weekend work rather than years.
- The schema gets battle-tested on real states before the plan engine
depends on it.
- Inventory, compliance, and drift-after-the-fact use cases are immediately
unlocked, which provides reason for users to adopt before the bigger
features land.
- The non-lock-in guarantee (round-trip fidelity) is established as a
core property up front, before users have a chance to assume otherwise.
Negative.
- v0 alone does not solve the original motivating problem (slow plans on
huge states). Users hitting that pain will need to wait for v1.
- The HTTP backend protocol is straightforward but does not expose Kilolock's
graph nature to Terraform itself. Terraform still asks for and writes the
whole blob over the wire on every plan/apply. Network round-trip overhead
is the v0 baseline; reducing it is a v1+ concern.