---
id: w2/m11/t001
title: "Design: route all public-surface creates through the internal store path instead of writing the App CR directly, per ADR003"
worker: worker2
status: todo
estimate: 45m
depends_on: []
---

## Objective

Decide and document the concrete design for routing `POST /v1/services`, GraphQL `createService`, and MCP `create_web_service`/`deploy` through the internal control-plane store instead of writing the `App` CR directly, so every app becomes store-managed.

## Context

- `lego/backend/internal/store/reconciler.go` (`projectSpec`) — the rows→CR projector; only apps created via the internal `POST /v1/apps` path go through this today.
- `lego/backend/internal/apps/` — public create/delete verbs (`Service.ensureCloneSecret` already exists for the public path's clone-token seam).
- `docs/ADR003-control-plane.md` — "Postgres is the source of truth" direction.
- `w2/005.md` (now `w2/m11`'s source note) for the full problem statement.

## Steps

1. Confirm the internal store API's create contract (fields, tenant scoping) that public creates would need to satisfy.
2. Decide how the store app-id label and tenant label get attached so the projector recognizes these as store-managed.
3. Decide how `spec.cloneSecret` minting (currently only in `apps.Service.ensureCloneSecret` on the public path) relates to the projector's own minting added in t003 — avoid duplicate/conflicting clone-secret logic.
4. Write up the design (where in the code each public surface's create handler changes, what it calls instead of `apps.Service.Create` writing the CR directly).

## Files

- `lego/backend/internal/apps/` (public create verbs)
- `lego/backend/internal/store/` (internal store API + reconciler)

## Acceptance criteria

- [ ] A written design covering all three public surfaces (REST/GraphQL/MCP) and how they'll route through the store.
- [ ] The clone-secret minting ownership question (public path vs projector) is resolved, not left ambiguous.

## Out of scope

- Implementation — that's t002/t003.
