Skip to content

Commit 5cf9901

Browse files
Implement Creer v1.3: optional mTLS transport and Docker Compose
Add CREER_SSL_* peer/server TLS settings with shared peer_httpx_client, dev cert + run scripts, docs/MTLS.md, and a two-node docker compose demo alongside HMAC trust from v1.2. Co-authored-by: Sanath S Patil <sanathpatil8861@gmail.com>
1 parent 216c5c3 commit 5cf9901

27 files changed

Lines changed: 401 additions & 192 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@
77
**/out/
88
*.pyc
99
.pytest_cache/
10+
certs/*.pem
11+
certs/*.key
12+
certs/*.csr
13+
certs/*.srl
14+
!certs/.gitkeep

PLAN.md

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,15 @@
22

33
## Done
44

5-
- **v0.1** — FastAPI planner/generator + VS Code write-to-workspace (+ optional git)
6-
- **v0.2** — Preview, GitHub create/push, templates, overwrite protection, `/creer` chat
7-
- **v0.3** — Streaming, offline/local models, bake-ins, SecretStorage + GIT_ASKPASS
8-
- **v0.4** — Stream cancel, selectable license/CI bake-ins, quality gates
9-
- **v0.5** — Content diff preview before write, multi-root workspace targeting, installable JSON/YAML template packs
10-
- **v0.6** — Pack marketplace + remote URL install/delete, side-by-side conflict diffs, publish packaging
11-
- **v0.7** — Self-hosted pack registry (`/registry` + download), extension icon, Browse Pack Registry, release changelog
12-
- **v0.8** — Federated registry (`/registry/federated` + peers), Browse Federated Registry UI, GitHub Actions release + CI (artifact-first; signed publish when secrets exist)
13-
- **v0.9** — Peer status/probe UX (`creer.registryPeers`, Manage Registry Peers), federated browse enrichment, GitHub Release on tag + `RELEASE.md`
14-
- **v1.0** — Stable foundation: optional registry write auth (Bearer / `X-Creer-Token`), `GET /registry/discover`, federated `discover=true`, SecretStorage registry token + Discover peers UX
15-
- **v1.1** — Discovery hardening: peer policy (SSRF / private IP blocks, allow/deny, max hops); extension surfaces policy errors, `creer.federationMaxHops` / `creer.warnPrivatePeers`, blocked-discover UX
16-
- **v1.2** — HMAC peer trust: backend `trust` blocks + peer_meta `trust_status` (`signed` | `unsigned` | `invalid` | `skipped`); extension `creer.requireSignedPeers`, trust badges/summaries; env `CREER_PEER_TRUST_SECRET` + `CREER_PEER_TRUST_MODE`
5+
- **v0.1–v1.0** — Core scaffold, templates/packs, streaming, GitHub, marketplace, federation, auth, discovery
6+
- **v1.1** — Discovery hardening (SSRF / allow-deny / hop budget)
7+
- **v1.2** — HMAC signed peer trust
8+
- **v1.3** — Optional TLS/mTLS transport for peers + Docker Compose multi-peer demo (`docs/MTLS.md`)
179

18-
## Optional next
10+
## Optional next (human)
1911

20-
- Human: configure `VSCE_PAT` / `OVSX_PAT` repository secrets; tag `v1.2.0` (see [`RELEASE.md`](RELEASE.md)) — agents cannot set GitHub Actions secrets
21-
- Mutual TLS between registries (optional future / human infra — not required for HMAC peer trust)
12+
- Configure `VSCE_PAT` / `OVSX_PAT` and tag a release ([`RELEASE.md`](RELEASE.md)) — agents cannot set GitHub Actions secrets
13+
- Production CA / cert rotation for mTLS deployments
2214

2315
## Non-goals
2416

README.md

Lines changed: 17 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,116 +2,41 @@
22

33
AI-powered repo scaffolding inside your workspace.
44

5-
**Current version: 1.2.0** (HMAC peer trust)
6-
7-
## Architecture
8-
9-
```
10-
creer/
11-
├── backend/ # Python FastAPI AI engine (+ packs/ + registry)
12-
├── extension/ # VS Code extension (icon in media/)
13-
└── .github/ # CI + release workflows
14-
```
5+
**Current version: 1.3.0**
156

167
## Quick start
178

189
```bash
19-
# Backend
10+
# Backend (local)
2011
cd backend && python -m venv venv && source venv/bin/activate
2112
pip install -r requirements.txt && cp .env.example .env
2213
uvicorn main:app --reload --port 8000
2314

15+
# Or Docker multi-peer demo
16+
docker compose up --build
17+
# A: http://localhost:8000 B: http://localhost:8001
18+
curl -s http://localhost:8000/registry/federated | head
19+
2420
# Extension
2521
cd extension && npm install && npm run compile
26-
# F5 → Creer: Create New Repo
27-
```
28-
29-
## Registry & federation (v0.7–v1.2)
30-
31-
Self-hosted pack catalog plus optional multi-host federation, write auth, peer policy, and HMAC peer trust:
32-
33-
| Method | Path | Description |
34-
|---|---|---|
35-
| `GET` | `/registry?q=&source=` | Searchable pack list (may include a `trust` block when signing is enabled) |
36-
| `GET` | `/registry/federated?q=&source=&peers=&discover=&max_hops=` | Local + peer merge; `discover=true` expands peers; `max_hops` caps hop depth (0–2); peers carry `trust_status` |
37-
| `GET` | `/registry/discover` | Peer discovery (policy-filtered; may include a `trust` block) |
38-
| `GET` | `/registry/peers` | Peer health + configured URLs (+ trust when verified) |
39-
| `POST` | `/registry/peers/probe` | Probe one peer `{ url }` (auth when configured; may 400 on policy; may include trust) |
40-
| `GET` | `/registry/packs/{id}` | Pack metadata |
41-
| `GET` | `/registry/packs/{id}/download` | Portable JSON pack (installable URL) |
42-
| `GET` | `/marketplace` | Curated featured view |
43-
| `POST` | `/packs/install` | Install pack from URL (auth when configured) |
44-
| `DELETE` | `/packs/{id}` | Delete installed pack (auth when configured) |
45-
46-
When the backend sets `CREER_REGISTRY_TOKEN`, mutating routes expect `Authorization: Bearer <token>` and/or `X-Creer-Token`.
47-
48-
### Peer policy env vars (v1.1)
49-
50-
Backend peer/federation policy (SSRF and private-IP hardening). The extension surfaces 400 `detail` strings and per-peer `error` / optional `policy` fields.
51-
52-
| Env | Purpose |
53-
|---|---|
54-
| `CREER_FEDERATION_MAX_HOPS` | Default max discovery hops (0–2; default 1). Extension may also send `max_hops` on federated browse when the backend accepts it. |
55-
| `CREER_PEER_ALLOWLIST` | Comma-separated hostnames/URLs; if non-empty, only these peers may be contacted |
56-
| `CREER_PEER_DENYLIST` | Comma-separated hostnames/URLs always blocked |
57-
| `CREER_ALLOW_PRIVATE_PEERS` | When true (`1`/`true`/`yes`), allow loopback/private/link-local peers (default off) |
58-
59-
### HMAC peer trust env vars (v1.2)
60-
61-
Shared-secret HMAC-SHA256 signing/verification for peer registry and discover responses. Federated `peer_meta` (and probe) include `trust_status`: `signed` | `unsigned` | `invalid` | `skipped`. Signed responses attach a `trust` block `{ alg: "HMAC-SHA256", kid: "default", sig: "<hex>" }`.
62-
63-
| Env | Purpose |
64-
|---|---|
65-
| `CREER_PEER_TRUST_SECRET` | Shared HMAC secret used to sign outbound registry/discover and verify peer payloads. Empty = signing/verification disabled. |
66-
| `CREER_PEER_TRUST_MODE` | `off` (default) \| `optional` \| `required`. `off`: never verify (outbound still signed when secret set). `optional`: verify when a signature is present; accept unsigned; mark `trust_status`. `required`: reject peer payloads without a valid HMAC (treated as fetch error). |
67-
68-
Mutual TLS between registries remains an **optional future** concern and is left to human/infra configuration — HMAC peer trust does not require mTLS.
69-
70-
Extension settings: `creer.registryPeers`, `creer.showPeerStatus`, `creer.federatedDiscover` (`discover=true`), `creer.federationMaxHops` (`max_hops`), `creer.warnPrivatePeers`, `creer.requireSignedPeers` (client-side filter of unsigned/invalid/skipped peer packs), `creer.registryToken` (deprecated plaintext — prefer SecretStorage).
71-
72-
Commands: **Browse Federated Registry**, **Manage Registry Peers** (Discover peers; private-host warning; policy-blocked suggestions; trust badges), **Set / Clear Registry Token**.
73-
74-
Install from another Creer host:
75-
76-
```bash
77-
curl -X POST http://localhost:8000/packs/install \
78-
-H 'Content-Type: application/json' \
79-
-d '{"url":"http://other-host:8000/registry/packs/fastapi-crud/download"}'
8022
```
8123

82-
Set `CREER_PUBLIC_BASE_URL` for absolute download links in registry responses.
83-
Set `CREER_REGISTRY_PEERS` for backend-configured federated discovery.
84-
Set `CREER_REGISTRY_TOKEN` to require write auth on install/delete/probe.
85-
Use `creer.registryPeers` in the extension for client-side extra peers when browsing.
86-
Set matching `CREER_PEER_TRUST_SECRET` (and `CREER_PEER_TRUST_MODE`) on peers to enable HMAC trust; enable `creer.requireSignedPeers` in the extension to hide unsigned peer packs.
87-
88-
## Extension commands
24+
## Highlights
8925

90-
| Command | Title |
26+
| Area | Features |
9127
|---|---|
92-
| `creer.createRepo` | Create New Repo |
93-
| `creer.createRepoFromChat` | Create from Chat Prompt |
94-
| `creer.browseMarketplace` | Browse Pack Marketplace |
95-
| `creer.browseRegistry` | Browse Pack Registry |
96-
| `creer.browseFederatedRegistry` | Browse Federated Registry |
97-
| `creer.manageRegistryPeers` | Manage Registry Peers |
98-
| `creer.installPackFromUrl` | Install Pack from URL |
99-
| `creer.setGitHubToken` / `clearGitHubToken` | GitHub SecretStorage token |
100-
| `creer.setRegistryToken` / `clearRegistryToken` | Registry write SecretStorage token |
28+
| Scaffold | Plan → preview → stream generate → content/conflict diffs → write |
29+
| Packs | Templates, JSON/YAML packs, marketplace, registry download |
30+
| Federation | Peers, discover, hop budget, SSRF policy, HMAC trust, optional mTLS |
31+
| Ops | Docker Compose, CI/release workflows, `RELEASE.md` |
10132

102-
## CI & publishing
33+
## TLS / mTLS
10334

104-
- **CI** (`.github/workflows/ci.yml`): pytest + extension compile on push/PR
105-
- **Release** (`.github/workflows/release.yml`): tag `v*` → package `.vsix`, create GitHub Release with attachment; publish to Marketplace / Open VSX only when `VSCE_PAT` / `OVSX_PAT` secrets are set
35+
See [`docs/MTLS.md`](docs/MTLS.md). Generate dev certs with `./scripts/gen-dev-certs.sh`, then set `CREER_SSL_*` and use `./scripts/run_backend.sh`.
10636

107-
See [`RELEASE.md`](RELEASE.md) and [`extension/PUBLISH.md`](extension/PUBLISH.md). Package locally:
108-
109-
```bash
110-
cd extension && npm run compile && npm run package
111-
# → creer-1.2.0.vsix (includes media/icon.png)
112-
```
37+
## Publishing
11338

114-
Signed Marketplace / Open VSX publish requires your own `VSCE_PAT` / `OVSX_PAT` (never commit tokens). Agents cannot set GitHub Actions secrets — that remains a human step.
39+
Human step for Marketplace / Open VSX tokens — [`RELEASE.md`](RELEASE.md).
11540

11641
## License
11742

RELEASE.md

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Releasing Creer v1.0.0
1+
# Releasing Creer
22

33
Exact steps for a human maintainer to cut a tagged release with GitHub Release + optional Marketplace / Open VSX publish.
44

@@ -8,51 +8,32 @@ In GitHub → **Settings → Secrets and variables → Actions**, add:
88

99
| Secret | Purpose |
1010
|---|---|
11-
| `VSCE_PAT` | Azure DevOps PAT with Marketplace **Acquire** + **Publish** (publisher must match `extension/package.json``publisher`) |
12-
| `OVSX_PAT` | Open VSX access token from [open-vsx.org](https://open-vsx.org/) |
11+
| `VSCE_PAT` | Azure DevOps PAT with Marketplace **Acquire** + **Publish** |
12+
| `OVSX_PAT` | Open VSX access token |
1313

14-
Both are optional. If neither is set, the release workflow still builds the `.vsix`, uploads it as an artifact, and (on tag pushes) creates a **GitHub Release** with the `.vsix` attached. Marketplace / Open VSX publish is skipped with `No publish tokens configured — artifact only`.
14+
Both optional. Without them the workflow still builds a `.vsix` artifact and (on tags) a GitHub Release.
1515

16-
Never commit PATs. Prefer repo secrets over exporting tokens in shared shells.
16+
**Cloud agents cannot configure these secrets.**
1717

18-
**Note:** Cloud agents cannot configure GitHub Actions secrets — a human must set `VSCE_PAT` / `OVSX_PAT` before signed Marketplace / Open VSX publish.
19-
20-
## 2. Bump & verify locally
18+
## 2. Verify locally
2119

2220
```bash
23-
# Confirm extension version is 1.0.0
24-
grep '"version"' extension/package.json
25-
26-
cd extension
27-
npm ci
28-
npm run compile
29-
npm run package
30-
# → creer-1.0.0.vsix
21+
grep '"version"' extension/package.json # e.g. 1.3.0
22+
cd extension && npm ci && npm run compile && npm run package
23+
# → creer-1.3.0.vsix
3124
```
3225

33-
Smoke-test: `code --install-extension creer-1.0.0.vsix` (or Cursor equivalent) against a running backend.
34-
35-
## 3. Tag v1.0.0 and push
36-
37-
From a clean `main` (or the release commit):
26+
## 3. Tag and push
3827

3928
```bash
40-
git tag -a v1.0.0 -m "Creer v1.0.0"
41-
git push origin v1.0.0
29+
git tag -a v1.3.0 -m "Creer v1.3.0"
30+
git push origin v1.3.0
4231
```
4332

44-
Tag pattern `v*` triggers [`.github/workflows/release.yml`](.github/workflows/release.yml).
45-
46-
## 4. What the workflow does
47-
48-
1. **build**`npm ci``compile``vsce package` → upload `creer-vsix` artifact
49-
2. **github-release** (tag pushes only) — create a GitHub Release and attach the `.vsix` (`contents: write`)
50-
3. **publish** — if `VSCE_PAT` / `OVSX_PAT` secrets exist, publish to Marketplace / Open VSX; otherwise artifact-only
33+
## 4. Workflow
5134

52-
You can also run the workflow via **Actions → Release → Run workflow** (`workflow_dispatch`) for a package/artifact without a tag (no GitHub Release job in that case).
35+
See `.github/workflows/release.yml` — build → GitHub Release on tags → optional Marketplace/Open VSX when secrets exist.
5336

5437
## 5. After release
5538

56-
- Confirm the GitHub Release page lists `creer-1.0.0.vsix`
57-
- If secrets were set, confirm Marketplace / Open VSX listing updated to 1.0.0
58-
- See [`extension/PUBLISH.md`](extension/PUBLISH.md) for manual `vsce` / `ovsx` publish from a laptop
39+
Confirm the GitHub Release lists the `.vsix`. If secrets were set, confirm Marketplace / Open VSX listings.

backend/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,13 @@ CREER_OFFLINE=
5757
# optional: verify when signature present; accept unsigned; mark trust_status
5858
# required: reject peer payloads without a valid HMAC (treated as fetch error)
5959
# CREER_PEER_TRUST_MODE=off
60+
61+
# Optional TLS for uvicorn (v1.3). Used by scripts/run_backend.sh.
62+
# CREER_SSL_CERTFILE=../certs/server.pem
63+
# CREER_SSL_KEYFILE=../certs/server-key.pem
64+
65+
# Optional mTLS client material for outbound peer calls (v1.3).
66+
# CREER_SSL_CA_CERTS=../certs/ca.pem
67+
# CREER_SSL_CLIENT_CERT=../certs/client.pem
68+
# CREER_SSL_CLIENT_KEY=../certs/client-key.pem
69+
# CREER_SSL_VERIFY=true

backend/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
ENV PYTHONDONTWRITEBYTECODE=1 \
6+
PYTHONUNBUFFERED=1 \
7+
PYTHONPATH=/app
8+
9+
COPY requirements.txt .
10+
RUN pip install --no-cache-dir -r requirements.txt
11+
12+
COPY . .
13+
14+
EXPOSE 8000
15+
16+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

backend/app/federation.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
from typing import Any
88
from urllib.parse import urljoin, urlparse
99

10-
import httpx
11-
1210
from config import CREER_PUBLIC_BASE_URL, CREER_REGISTRY_PEERS
1311
from app.auth import registry_auth_required
1412
from app.peer_policy import (
@@ -18,9 +16,10 @@
1816
policy_summary,
1917
)
2018
from app.peer_trust import evaluate_peer_trust, sign_payload, trust_mode
19+
from app.http_client import peer_httpx_client
2120
from app.registry import list_registry, registry_count
2221

23-
FEDERATION_VERSION = "1.2.0"
22+
FEDERATION_VERSION = "1.3.0"
2423
_MAX_PEERS = 8
2524
_DISCOVER_TIMEOUT = 3.0
2625

@@ -172,7 +171,7 @@ def _fetch_peer_registry(
172171

173172
url = f"{base}/registry"
174173
try:
175-
with httpx.Client(timeout=timeout, follow_redirects=True) as client:
174+
with peer_httpx_client(timeout=timeout) as client:
176175
resp = client.get(url, params=params or None)
177176
resp.raise_for_status()
178177
data = resp.json()
@@ -222,7 +221,7 @@ def fetch_peer_discover(
222221

223222
url = f"{base}/registry/discover"
224223
try:
225-
with httpx.Client(timeout=timeout, follow_redirects=True) as client:
224+
with peer_httpx_client(timeout=timeout) as client:
226225
resp = client.get(url)
227226
resp.raise_for_status()
228227
data = resp.json()
@@ -374,7 +373,7 @@ def probe_peer(base_url: str, timeout: float = 5.0) -> dict[str, Any]:
374373
trust_status: str | None = None
375374

376375
try:
377-
with httpx.Client(timeout=timeout, follow_redirects=True) as client:
376+
with peer_httpx_client(timeout=timeout) as client:
378377
# Prefer /health for liveness + version (+ registry_count when present)
379378
try:
380379
hresp = client.get(f"{base}/health")

backend/app/http_client.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Shared httpx client for outbound peer calls (optional TLS / mTLS)."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
import httpx
8+
9+
from config import (
10+
CREER_SSL_CA_CERTS,
11+
CREER_SSL_CLIENT_CERT,
12+
CREER_SSL_CLIENT_KEY,
13+
CREER_SSL_VERIFY,
14+
)
15+
16+
17+
def peer_httpx_client(timeout: float = 8.0) -> httpx.Client:
18+
"""
19+
Build an httpx.Client for federation/peer requests.
20+
21+
- verify: CA bundle path, True, or False (CREER_SSL_VERIFY)
22+
- cert: client cert/key tuple when both CREER_SSL_CLIENT_* are set
23+
"""
24+
verify: Any
25+
if not CREER_SSL_VERIFY:
26+
verify = False
27+
elif CREER_SSL_CA_CERTS:
28+
verify = CREER_SSL_CA_CERTS
29+
else:
30+
verify = True
31+
32+
kwargs: dict[str, Any] = {
33+
"timeout": timeout,
34+
"follow_redirects": True,
35+
"verify": verify,
36+
}
37+
if CREER_SSL_CLIENT_CERT and CREER_SSL_CLIENT_KEY:
38+
kwargs["cert"] = (CREER_SSL_CLIENT_CERT, CREER_SSL_CLIENT_KEY)
39+
40+
return httpx.Client(**kwargs)

backend/app/registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def matches(item: dict[str, Any]) -> bool:
104104

105105
return sign_payload(
106106
{
107-
"version": "1.2.0",
107+
"version": "1.3.0",
108108
"base_url": CREER_PUBLIC_BASE_URL or None,
109109
"items": items,
110110
}

backend/config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,24 @@ def _clamp_federation_max_hops(raw: str | None) -> int:
4747
CREER_PEER_TRUST_SECRET = os.getenv("CREER_PEER_TRUST_SECRET", "").strip()
4848
# Peer trust verification mode: off | optional | required (default off)
4949
CREER_PEER_TRUST_MODE = os.getenv("CREER_PEER_TRUST_MODE", "off").strip().lower() or "off"
50+
51+
# Optional TLS for uvicorn / peer mTLS (v1.3+)
52+
CREER_SSL_CERTFILE = os.getenv("CREER_SSL_CERTFILE", "").strip() or None
53+
CREER_SSL_KEYFILE = os.getenv("CREER_SSL_KEYFILE", "").strip() or None
54+
CREER_SSL_CA_CERTS = os.getenv("CREER_SSL_CA_CERTS", "").strip() or None
55+
CREER_SSL_CLIENT_CERT = os.getenv("CREER_SSL_CLIENT_CERT", "").strip() or None
56+
CREER_SSL_CLIENT_KEY = os.getenv("CREER_SSL_CLIENT_KEY", "").strip() or None
57+
# Verify peer TLS certificates (default true). Set false only for broken local experiments.
58+
CREER_SSL_VERIFY = os.getenv("CREER_SSL_VERIFY", "true").lower() not in (
59+
"0",
60+
"false",
61+
"no",
62+
)
63+
64+
65+
def tls_server_configured() -> bool:
66+
return bool(CREER_SSL_CERTFILE and CREER_SSL_KEYFILE)
67+
68+
69+
def mtls_client_configured() -> bool:
70+
return bool(CREER_SSL_CLIENT_CERT and CREER_SSL_CLIENT_KEY)

0 commit comments

Comments
 (0)