Compatibility Policy

What DevCloud v1.0 promises, and what it deliberately does not.

This covers the surfaces you touch as a user: the config file, the environment, the CLI, the admin API, and the AWS wire protocol. For the in-tree Go contract that service implementations are written against, see plugin-api.md.

Versions follow Semantic Versioning. “Across 1.x” means every release from v1.0.0 up to but not including v2.0.0.

Guaranteed across 1.x

Configuration file

These keys keep their name, type and meaning. New keys may be added; existing ones are not removed or repurposed. Defined in internal/config/config.go.

KeyTypeMeaning
server.portintListen port. Default 4747 when absent or 0.
servicesmapPresence of the block is authoritative — only the services it lists run, and an empty block runs nothing. Absent means every registered service runs.
services.<id>.enabledboolWhether that service starts.
services.<id>.data_dirstringWhere that service stores data.
providers.aws.servicesmapThe same block as services, namespaced by provider. Identical semantics; wins over services when both are written, with a warning.
admin.enabledboolWhether the admin API is served. Default false.
logging.levelstringLog level.
logging.formatstringLog format.

Environment variables

VariableMeaning
DEVCLOUD_PORTOverrides server.port.
DEVCLOUD_SERVICESNames the running service set. all, a comma-separated list of service ids, or the tier1 / tier2 / tier3 shortcuts. Unknown tokens are treated as literal service names.
DEVCLOUD_DATA_DIRBase directory; each service stores under <base>/<id>. Overrides data_dir.

Environment overrides config file, and that precedence is guaranteed.

When DEVCLOUD_SERVICES names services, it decides membership on its own: it starts a service the services block omits and stops one the block enables. It replaces the block’s selection rather than intersecting with it. The literal all is the exception — it switches the filter off and hands the decision back to the services block, which is what “all” has always meant here (configuration.md).

Command line

-config <path> keeps its meaning. With no flag, DevCloud uses ./devcloud.yaml if present and the embedded defaults otherwise — zero-config startup keeps working.

Admin API

Served at /devcloud/api/ when admin.enabled: true. These routes keep responding, and their JSON responses only gain fields — no documented key is removed or repurposed.

RouteGuaranteed response keys
GET /devcloud/api/servicesarray of id, name, status, resourceCount
GET /devcloud/api/services/{id}/resourcesarray of type, id, name
GET /devcloud/api/logsarray of method, path, status, duration, timestamp, service; newest first; ?limit= honoured
GET /devcloud/api/fidelityobject keyed by service id, each with modelBacked and counts; ?service=<id> adds operations

Fidelity tier names

hand-verified, auto-crud and unimplemented keep the meanings given in fidelity-manifest.md. The set does not shrink, and a name is never reused for a different meaning.

Every operation the manifest lists carries a tier from that set, every registered service appears, and every operation the CRUD engine serves is present and not filed as unimplemented — all three fail the build, in cmd/devcloud/fidelity_test.go.

What no test can catch is an operation that never reaches the manifest at all. That is bounded rather than eliminated: for the 194 services with an in-tree Smithy model the operation universe comes from the model, so an operation losing its implementation reclassifies to unimplemented instead of disappearing. For the 11 without one, the universe is what the providers serve, so the manifest lists no unimplemented tail for them. modelBacked on GET /devcloud/api/fidelity reports which is which.

Wire behaviour — scoped to the compatibility suite

Whatever a test in tests/compatibility/ asserts about a response keeps holding across 1.x — that property, and nothing wider.

The promise is as wide as each individual assertion: not as wide as the field, and not as wide as the operation. CreateFunction in test_lambda.py shows all three cases at once:

FieldAsserted asPromised
FunctionNameequal to the name sentkey and value
FunctionArnpresentpresence only — not that it stays ARN-shaped
Runtime, Handler, MemorySizenot assertednothing, though today’s response includes them

That narrowness is the point: it is the promise the repo can actually keep. The suite — 1,144 tests driving real boto3 clients — runs in CI on every push and again against the tagged commit before a release publishes, so breaking an assertion fails the build rather than depending on review discipline. Anything the suite does not assert rests on nothing but intent. Widening the promise means adding or strengthening assertions, and such contributions are welcome.

Not guaranteed

Depending on any of the following will break, and breaking it is not a major-version event.

  • auto-crud response content. 5,193 operations are served by the generic CRUD engine at fidelity that is deliberately plausible, not faithful: store-backed responses echoing your input plus synthesized ids and ARNs, with no validation, no cross-resource integrity, no pagination correctness and no business logic. Their shape and content may change in any release. Use them to wire an SDK up, nothing more.
  • Hand-verified operations with no compatibility test. Of 4,497 hand-verified operations, only what the suite covers is promised. The rest are best-effort.
  • Data durability. Stores are local development stores. Several are in-memory and per-process; on-disk layouts under data_dir may change format between releases without a migration. Do not treat DevCloud as a database.
  • unimplemented → served transitions. An operation that returns an error today may start returning a response. This is additive, and ships in a minor release.
  • Service coverage. New services may be added in a minor release. The 205 services registered today are a floor, not a ceiling — and not a promise of depth either: 4 of them serve no operation and only decline cleanly. See coverage.md.
  • Error codes, HTTP status and message wording. What is guaranteed for an unimplemented operation is that it fails — an AWS-shaped error, never a fabricated success. Which error is not guaranteed: it comes from whichever provider handles the request, and sqs even differs by protocol. fidelity-manifest.md records the current behaviour; normalizing it is a minor release, not a major one.
  • Log output. Format, levels and wording of server logs are operational, not an API.
  • Everything under internal/. Go forbids importing it from another module, so DevCloud restructures it freely across 1.x. The Phase 2 refactor is the precedent: the IR, ModelSource, ProviderScoped and the auth adapters all landed inside a 1.x minor without a compatibility event, because nothing outside this module can reach them. The in-tree ServicePlugin contract in plugin-api.md is no exception — it is a convention that keeps in-tree plugins compiling, and it does not gate release versioning.
  • Behavioural parity with AWS. No release of DevCloud promises AWS’s validation, business logic, eventual-consistency timing, rate limits, or IAM enforcement. Credentials are accepted without signature verification.

Deprecation procedure

Removing anything from the guaranteed list is a major version bump. Before that can happen:

  1. Deprecate in a minor release. The old form keeps working and emits a runtime warning naming its replacement. The precedent is the dashboardadmin config rename: the old key still enables the admin API, warns, and yields to an explicit admin block (config.go).
  2. Document it — in the release notes for that version, and here.
  3. Remove no earlier than the next major. At least one released version must have shipped the warning.

Silence is not deprecation. A removed key that YAML would otherwise drop without comment is kept in the parser purely to warn — that is why auth still produces a message telling you SigV4 is not enforced rather than being ignored.

The pre-flight checklist in RELEASE.md makes this a step in cutting a release, not a thing to remember.

Reporting a break

If a 1.x release breaks something on the guaranteed list, that is a bug — please open an issue with the DevCloud version and a reproducing snippet. If it breaks something on the not-guaranteed list, an issue is still useful: it is evidence for tightening the policy in a future major.