Align naming with coulomb.social reuse-surface conventions
Some checks failed
ci / validate-registry (push) Has been cancelled

Use reuse.coulomb.social, REUSE_SURFACE_URL/TOKEN env vars, reuse-surface
image and reuse-surface-env secret. Replace reuse-surface-hub entrypoint with
reuse-surface serve; CLI uses --base-url.
This commit is contained in:
2026-06-15 09:02:02 +02:00
parent 4f98506f18
commit cbcd097214
13 changed files with 120 additions and 100 deletions

View File

@@ -130,11 +130,11 @@ artifacts.
.venv/bin/reuse-surface federation compose
.venv/bin/reuse-surface graph --check
# Federation hub service (local)
# REUSE_SURFACE_HUB_TOKEN=dev-token reuse-surface-hub
# Federation service (local)
# REUSE_SURFACE_TOKEN=dev-token reuse-surface serve
# Hub CLI (against deployed or local hub)
# REUSE_SURFACE_HUB_URL=http://127.0.0.1:8000 reuse-surface hub status
# Hub CLI (against deployed or local service)
# REUSE_SURFACE_URL=http://127.0.0.1:8000 reuse-surface hub status
# Automated tests
.venv/bin/pytest -q

View File

@@ -8,11 +8,11 @@ COPY schemas ./schemas
RUN pip install --no-cache-dir .
ENV REUSE_SURFACE_HUB_HOST=0.0.0.0
ENV REUSE_SURFACE_HUB_PORT=8000
ENV REUSE_SURFACE_HUB_DB=/data/hub.db
ENV REUSE_SURFACE_HUB_CACHE_DIR=/data/cache
ENV REUSE_SURFACE_HOST=0.0.0.0
ENV REUSE_SURFACE_PORT=8000
ENV REUSE_SURFACE_DB=/data/reuse.db
ENV REUSE_SURFACE_CACHE_DIR=/data/cache
EXPOSE 8000
CMD ["reuse-surface-hub"]
CMD ["reuse-surface", "serve"]

View File

@@ -1,32 +0,0 @@
# Federation Hub — Kubernetes Deployment
Companion to **RAILIANCE-WP-0007** (`railiance-apps` Helm release).
## Image
```bash
docker build -t gitea.coulomb.social/coulomb/reuse-surface-hub:<tag> .
docker push gitea.coulomb.social/coulomb/reuse-surface-hub:<tag>
```
## Required environment
| Variable | Purpose |
|---|---|
| `REUSE_SURFACE_HUB_TOKEN` | Bearer token for write API |
| `REUSE_SURFACE_HUB_DB` | SQLite path (default `/data/hub.db`) |
| `REUSE_SURFACE_HUB_CACHE_DIR` | Remote index cache (default `/data/cache`) |
Mount a PVC at `/data` for persistence.
## Probes
- Liveness/readiness: `GET /health` on port `8000`
## Client configuration
```bash
export REUSE_SURFACE_HUB_URL=https://reuse-hub.whywhynot.de
export REUSE_SURFACE_HUB_TOKEN=<write-token>
reuse-surface hub status
```

View File

@@ -0,0 +1,39 @@
# reuse-surface Service — Kubernetes Deployment
Companion to **RAILIANCE-WP-0007** (`railiance-apps` Helm release).
## Image
Repository: `gitea.coulomb.social/coulomb/reuse-surface` (Gitea org `coulomb`, repo `reuse-surface`).
```bash
docker build -t gitea.coulomb.social/coulomb/reuse-surface:<tag> .
docker push gitea.coulomb.social/coulomb/reuse-surface:<tag>
```
## Required environment
| Variable | Purpose |
|---|---|
| `REUSE_SURFACE_TOKEN` | Bearer token for write API |
| `REUSE_SURFACE_DB` | SQLite path (default `/data/reuse.db`) |
| `REUSE_SURFACE_CACHE_DIR` | Remote index cache (default `/data/cache`) |
Mount a PVC at `/data` for persistence. Inject secrets via Kubernetes Secret
`reuse-surface-env`.
## Probes
- Liveness/readiness: `GET /health` on port `8000`
## Public URL
`https://reuse.coulomb.social`
## Client configuration
```bash
export REUSE_SURFACE_URL=https://reuse.coulomb.social
export REUSE_SURFACE_TOKEN=<write-token>
reuse-surface hub status
```

View File

@@ -23,7 +23,6 @@ dev = [
[project.scripts]
reuse-surface = "reuse_surface.cli:main"
reuse-surface-hub = "reuse_surface.hub.app:main"
[tool.setuptools.packages.find]
where = ["."]

View File

@@ -192,13 +192,20 @@ def cmd_catalog(args: argparse.Namespace) -> int:
return 0
def _hub_url(args: argparse.Namespace) -> str | None:
return getattr(args, "hub_url", None)
def _service_url(args: argparse.Namespace) -> str | None:
return getattr(args, "base_url", None)
def cmd_serve(args: argparse.Namespace) -> int:
from reuse_surface.hub.app import main as serve_main
serve_main()
return 0
def cmd_hub_status(args: argparse.Namespace) -> int:
try:
status, payload = hub_client.hub_status(_hub_url(args))
status, payload = hub_client.hub_status(_service_url(args))
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -211,7 +218,7 @@ def cmd_hub_status(args: argparse.Namespace) -> int:
def cmd_hub_list(args: argparse.Namespace) -> int:
try:
status, payload = hub_client.hub_list(_hub_url(args))
status, payload = hub_client.hub_list(_service_url(args))
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -227,7 +234,7 @@ def cmd_hub_list(args: argparse.Namespace) -> int:
def cmd_hub_show(args: argparse.Namespace) -> int:
try:
status, payload = hub_client.hub_show(args.repo, _hub_url(args))
status, payload = hub_client.hub_show(args.repo, _service_url(args))
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -249,7 +256,7 @@ def cmd_hub_register(args: argparse.Namespace) -> int:
if args.description:
body["description"] = args.description
try:
status, payload = hub_client.hub_register(body, _hub_url(args))
status, payload = hub_client.hub_register(body, _service_url(args))
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -276,7 +283,7 @@ def cmd_hub_update(args: argparse.Namespace) -> int:
print("error: no fields to update", file=sys.stderr)
return 1
try:
status, payload = hub_client.hub_update(args.repo, body, _hub_url(args))
status, payload = hub_client.hub_update(args.repo, body, _service_url(args))
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -412,10 +419,13 @@ def main(argv: list[str] | None = None) -> int:
)
graph.set_defaults(func=cmd_graph)
hub = subparsers.add_parser("hub", help="federation hub client")
serve = subparsers.add_parser("serve", help="run federation service API")
serve.set_defaults(func=cmd_serve)
hub = subparsers.add_parser("hub", help="federation service client")
hub.add_argument(
"--hub-url",
help="hub base URL (or set REUSE_SURFACE_HUB_URL)",
"--base-url",
help="service base URL (or set REUSE_SURFACE_URL)",
)
hub_sub = hub.add_subparsers(dest="hub_command", required=True)

View File

@@ -15,15 +15,15 @@ HUB_VERSION = "0.1.0"
def _db_path() -> Path:
return Path(os.environ.get("REUSE_SURFACE_HUB_DB", "/data/hub.db"))
return Path(os.environ.get("REUSE_SURFACE_DB", "/data/reuse.db"))
def _cache_dir() -> Path:
return Path(os.environ.get("REUSE_SURFACE_HUB_CACHE_DIR", "/data/cache"))
return Path(os.environ.get("REUSE_SURFACE_CACHE_DIR", "/data/cache"))
def _write_token() -> str:
return os.environ.get("REUSE_SURFACE_HUB_TOKEN", "")
return os.environ.get("REUSE_SURFACE_TOKEN", "")
def _store() -> HubStore:
@@ -41,7 +41,7 @@ def _require_auth(authorization: str | None = Header(default=None)) -> None:
write_token = _write_token()
if not write_token:
raise _http_error(
503, "misconfigured", "REUSE_SURFACE_HUB_TOKEN is not configured"
503, "misconfigured", "REUSE_SURFACE_TOKEN is not configured"
)
if not authorization or not authorization.startswith("Bearer "):
raise _http_error(401, "unauthorized", "Bearer token required")
@@ -56,7 +56,7 @@ def create_app() -> FastAPI:
@app.get("/health")
def health() -> dict[str, str]:
return {"status": "ok", "service": "reuse-surface-hub", "version": HUB_VERSION}
return {"status": "ok", "service": "reuse-surface", "version": HUB_VERSION}
@app.get("/v1/repos")
def list_repos() -> dict[str, Any]:
@@ -138,6 +138,6 @@ def create_app() -> FastAPI:
def main() -> None:
import uvicorn
host = os.environ.get("REUSE_SURFACE_HUB_HOST", "0.0.0.0")
port = int(os.environ.get("REUSE_SURFACE_HUB_PORT", "8000"))
host = os.environ.get("REUSE_SURFACE_HOST", "0.0.0.0")
port = int(os.environ.get("REUSE_SURFACE_PORT", "8000"))
uvicorn.run(create_app(), host=host, port=port, reload=False)

View File

@@ -7,7 +7,7 @@ from typing import Any
from reuse_surface.federation import compose_federated_index
from reuse_surface.hub.store import HubStore
DEFAULT_DOMAIN = os.environ.get("REUSE_SURFACE_HUB_DOMAIN", "helix_forge")
DEFAULT_DOMAIN = os.environ.get("REUSE_SURFACE_DOMAIN", "helix_forge")
def registrations_to_manifest(

View File

@@ -7,17 +7,17 @@ import urllib.request
from typing import Any
def hub_base_url(explicit: str | None = None) -> str:
base = (explicit or os.environ.get("REUSE_SURFACE_HUB_URL", "")).rstrip("/")
def service_base_url(explicit: str | None = None) -> str:
base = (explicit or os.environ.get("REUSE_SURFACE_URL", "")).rstrip("/")
if not base:
raise ValueError(
"hub URL not configured; set REUSE_SURFACE_HUB_URL or pass --hub-url"
"service URL not configured; set REUSE_SURFACE_URL or pass --base-url"
)
return base
def hub_token() -> str | None:
return os.environ.get("REUSE_SURFACE_HUB_TOKEN")
def service_token() -> str | None:
return os.environ.get("REUSE_SURFACE_TOKEN")
def _request(
@@ -49,24 +49,24 @@ def _request(
def hub_status(base_url: str | None = None) -> tuple[int, Any]:
return _request("GET", f"{hub_base_url(base_url)}/health")
return _request("GET", f"{service_base_url(base_url)}/health")
def hub_list(base_url: str | None = None) -> tuple[int, Any]:
return _request("GET", f"{hub_base_url(base_url)}/v1/repos")
return _request("GET", f"{service_base_url(base_url)}/v1/repos")
def hub_show(repo: str, base_url: str | None = None) -> tuple[int, Any]:
return _request("GET", f"{hub_base_url(base_url)}/v1/repos/{repo}")
return _request("GET", f"{service_base_url(base_url)}/v1/repos/{repo}")
def hub_register(payload: dict[str, Any], base_url: str | None = None) -> tuple[int, Any]:
token = hub_token()
token = service_token()
if not token:
raise ValueError("REUSE_SURFACE_HUB_TOKEN is required for register")
raise ValueError("REUSE_SURFACE_TOKEN is required for register")
return _request(
"POST",
f"{hub_base_url(base_url)}/v1/repos",
f"{service_base_url(base_url)}/v1/repos",
token=token,
body=payload,
)
@@ -75,12 +75,12 @@ def hub_register(payload: dict[str, Any], base_url: str | None = None) -> tuple[
def hub_update(
repo: str, payload: dict[str, Any], base_url: str | None = None
) -> tuple[int, Any]:
token = hub_token()
token = service_token()
if not token:
raise ValueError("REUSE_SURFACE_HUB_TOKEN is required for update")
raise ValueError("REUSE_SURFACE_TOKEN is required for update")
return _request(
"PATCH",
f"{hub_base_url(base_url)}/v1/repos/{repo}",
f"{service_base_url(base_url)}/v1/repos/{repo}",
token=token,
body=payload,
)

View File

@@ -21,7 +21,7 @@ Companion deployment workplan: `railiance-apps` **RAILIANCE-WP-0007**.
| Item | Value |
|---|---|
| Default production URL | `https://reuse-hub.whywhynot.de` (confirm at deploy) |
| Default production URL | `https://reuse.coulomb.social` |
| API prefix | `/v1` |
| Read formats | JSON (default), YAML via `Accept: application/yaml` or `?format=yaml` |
| Write content type | `application/json` |
@@ -30,8 +30,8 @@ Environment variables for clients:
| Variable | Purpose |
|---|---|
| `REUSE_SURFACE_HUB_URL` | Hub base URL (no trailing slash) |
| `REUSE_SURFACE_HUB_TOKEN` | Bearer token for write operations |
| `REUSE_SURFACE_URL` | Service base URL (no trailing slash) |
| `REUSE_SURFACE_TOKEN` | Bearer token for write operations |
---
@@ -45,7 +45,7 @@ Environment variables for clients:
Write requests must include:
```http
Authorization: Bearer <REUSE_SURFACE_HUB_TOKEN>
Authorization: Bearer <REUSE_SURFACE_TOKEN>
```
Missing or invalid token → `401 Unauthorized`.
@@ -88,7 +88,7 @@ Liveness/readiness probe.
```json
{
"status": "ok",
"service": "reuse-surface-hub",
"service": "reuse-surface",
"version": "0.1.0"
}
```
@@ -232,10 +232,10 @@ Non-2xx responses use:
| Env var | Required | Purpose |
|---|---|---|
| `REUSE_SURFACE_HUB_TOKEN` | yes | Write API bearer token |
| `REUSE_SURFACE_HUB_DB` | no | SQLite path (default `/data/hub.db`) |
| `REUSE_SURFACE_HUB_CACHE_DIR` | no | Remote index cache (default `/data/cache`) |
| `REUSE_SURFACE_HUB_DOMAIN` | no | Default federated `domain` (default `helix_forge`) |
| `REUSE_SURFACE_TOKEN` | yes | Write API bearer token |
| `REUSE_SURFACE_DB` | no | SQLite path (default `/data/reuse.db`) |
| `REUSE_SURFACE_CACHE_DIR` | no | Remote index cache (default `/data/cache`) |
| `REUSE_SURFACE_DOMAIN` | no | Default federated `domain` (default `helix_forge`) |
---
@@ -249,13 +249,16 @@ Non-2xx responses use:
| `reuse-surface hub register ...` | `POST /v1/repos` |
| `reuse-surface hub update ...` | `PATCH /v1/repos/{repo}` |
Global flags: `--hub-url`, env `REUSE_SURFACE_HUB_URL`, `REUSE_SURFACE_HUB_TOKEN`.
Run locally: `reuse-surface serve`. Global client flags: `--base-url`, env
`REUSE_SURFACE_URL`, `REUSE_SURFACE_TOKEN`.
---
## 9. Deployment reference
- Image: `gitea.coulomb.social/coulomb/reuse-surface-hub:<tag>`
- Image: `gitea.coulomb.social/coulomb/reuse-surface:<tag>`
- Public URL: `https://reuse.coulomb.social`
- Secret: `reuse-surface-env` with `REUSE_SURFACE_TOKEN`
- Probe path: `/health`
- Persistence: PVC at `/data` (SQLite + fetch cache)
- Helm release: `railiance-apps` RAILIANCE-WP-0007

View File

@@ -32,9 +32,9 @@ capabilities:
def hub_client(tmp_path, monkeypatch):
db_path = tmp_path / "hub.db"
cache_dir = tmp_path / "cache"
monkeypatch.setenv("REUSE_SURFACE_HUB_TOKEN", "test-token")
monkeypatch.setenv("REUSE_SURFACE_HUB_DB", str(db_path))
monkeypatch.setenv("REUSE_SURFACE_HUB_CACHE_DIR", str(cache_dir))
monkeypatch.setenv("REUSE_SURFACE_TOKEN", "test-token")
monkeypatch.setenv("REUSE_SURFACE_DB", str(db_path))
monkeypatch.setenv("REUSE_SURFACE_CACHE_DIR", str(cache_dir))
app = create_app()
with TestClient(app) as client:
yield client

View File

@@ -92,15 +92,15 @@ Writes `docs/graph/capability-graph.mmd` and `docs/graph/index.html`.
Client for the federation hub service (REUSE-WP-0011).
```bash
export REUSE_SURFACE_HUB_URL=https://reuse-hub.whywhynot.de
export REUSE_SURFACE_HUB_TOKEN=<write-token>
export REUSE_SURFACE_URL=https://reuse.coulomb.social
export REUSE_SURFACE_TOKEN=<write-token>
reuse-surface hub status
reuse-surface hub list
reuse-surface hub register --repo state-hub --url https://.../capabilities.yaml
reuse-surface hub update --repo state-hub --enabled true
```
Run the hub service locally: `reuse-surface-hub` (requires `REUSE_SURFACE_HUB_TOKEN`).
Run the service locally: `REUSE_SURFACE_TOKEN=dev-token reuse-surface serve`
## Export format

View File

@@ -44,7 +44,7 @@ for discovery without cloning every repo.
| Operations | Container image, deployment manifests, TLS ingress, documented runbook |
| Dogfood | `reuse-surface` and at least one sibling repo registered via the hub |
**Proposed public URL (confirm in T05):** `https://reuse-hub.whywhynot.de`
**Public URL:** `https://reuse.coulomb.social`
## Design decisions (draft)
@@ -59,7 +59,7 @@ for discovery without cloning every repo.
`url` sources (reuse WP-0010 fetch/cache logic) and merges into
`GET /v1/federated` output. Local-only `index` paths are **not** valid hub
registrations unless expressed as published raw URLs.
- **Auth (MVP):** Token-based write access via `REUSE_SURFACE_HUB_TOKEN` /
- **Auth (MVP):** Token-based write access via `REUSE_SURFACE_TOKEN` /
`Authorization: Bearer`; read endpoints public for agent discovery.
- **Persistence (MVP):** SQLite on a PVC inside the hub container. Postgres
via cnpg is a follow-up if multi-replica or backup requirements emerge.
@@ -100,7 +100,7 @@ plus hub metadata (`registered_at`, `updated_at`, `registered_by`).
## CLI sketch (T03 refines)
```bash
# Configure hub endpoint (env REUSE_SURFACE_HUB_URL or --hub-url)
# Configure service URL (env REUSE_SURFACE_URL or --base-url)
reuse-surface hub status
reuse-surface hub list
reuse-surface hub register --repo state-hub \
@@ -167,8 +167,9 @@ state_hub_task_id: "38fec6ce-23c0-4157-8350-7d112b9e8264"
Extend `reuse-surface` CLI with `hub` subcommands:
- `register`, `update`, `show`, `list`, `status`
- `--hub-url` flag and `REUSE_SURFACE_HUB_URL` env support
- `REUSE_SURFACE_HUB_TOKEN` for authenticated writes
- `--base-url` flag and `REUSE_SURFACE_URL` env support
- `REUSE_SURFACE_TOKEN` for authenticated writes
- `reuse-surface serve` to run the API locally or in container
- Document in `tools/README.md` and `AGENTS.md`
## Containerize And Publish Deployment Artifacts
@@ -183,8 +184,8 @@ state_hub_task_id: "24eec9ad-21fc-4f0b-8671-72d955b15e68"
Provide:
- `Dockerfile` for the hub service
- Example k8s manifests or Helm values template under `deploy/` or `docs/deploy/`
- Image naming convention: `gitea.coulomb.social/coulomb/reuse-surface-hub:<tag>`
- Example k8s manifests or Helm values template under `docs/deploy/`
- Image: `gitea.coulomb.social/coulomb/reuse-surface:<tag>`
- CI job or documented build/push steps (coordinate with `railiance-forge`
registry guidance)
@@ -199,10 +200,10 @@ state_hub_task_id: "7f26a70f-7b7d-413d-8162-931c6dffef6a"
Deploy the hub as a governed release on `railiance01`:
- Confirm hostname (default `reuse-hub.whywhynot.de`) and DNS A record
- Confirm DNS for `reuse.coulomb.social` (coulomb.social zone)
- Traefik ingress + cert-manager TLS
- PVC for SQLite data
- Inject hub write token via SOPS/sealed secret
- Inject `REUSE_SURFACE_TOKEN` via Secret `reuse-surface-env` (SOPS handoff)
- Verify `GET /health` and `GET /v1/federated` from workstation and from cluster
**Blocked on:** DNS decision, operator secret provisioning, and