Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions deploy/hf-space/three-node/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Three-node demo — deploy runbook (owner-gated)

Publishes the three-node topology (ADR 0012 / `docs/three-node-demo-topology.md`)
as **three** Hugging Face Docker Spaces under the `liovina` account:

| Node | Space | Role env | Branch |
|--------|------------------------------|----------------|--------|
| Center | `liovina/agentflow-center` | `center` | `msk` |
| Edge 1 | `liovina/agentflow-edge-spb` | `edge` | `spb` |
| Edge 2 | `liovina/agentflow-edge-ekb` | `edge` | `ekb` |

All three build the **same image** — the existing `deploy/hf-space/Dockerfile`,
tracking `main`. Role is pure environment set in each Space's **Settings**, not
in the image. The standalone `liovina/agentflow-demo` Space is unaffected; this
set is additive.

Each Space repo root gets two files: the shared `Dockerfile` and that node's
`README.md` (from `center/`, `edge-spb/`, `edge-ekb/` here).

## Deploy (outward — OWNER GATE)

Creating/pushing public Spaces under `liovina` is an external publish and is an
**owner gate**. The HF token lives in `D:/VacancyRadar/.env` (`HF_TOKEN`); never
print it. Bring-up order is **center first** — the edges need its URL live and a
matching `AGENTFLOW_NODE_TOKEN`.

Pick one strong shared token value for `AGENTFLOW_NODE_TOKEN` (same on all three;
store as a Space **secret**, never a variable, never logged).

```bash
# For each of: agentflow-center, agentflow-edge-spb, agentflow-edge-ekb
NAME=agentflow-center # then edge-spb, edge-ekb
ROLEDIR=center # then edge-spb, edge-ekb

# 1. Create the Space (one-time)
huggingface-cli repo create "$NAME" --repo-type space --space_sdk docker

# 2. Push the shared Dockerfile + this node's README to the Space repo root
git clone "https://huggingface.co/spaces/liovina/$NAME" "/tmp/$NAME"
cp deploy/hf-space/Dockerfile "/tmp/$NAME/Dockerfile"
cp "deploy/hf-space/three-node/$ROLEDIR/README.md" "/tmp/$NAME/README.md"
cd "/tmp/$NAME" && git add Dockerfile README.md \
&& git commit -m "AgentFlow $NAME (three-node demo)" && git push
```

Then, in each Space's **Settings → Variables and secrets**, set the environment
from that node's README table:

- **Center:** `AGENTFLOW_NODE_ROLE=center`, `AGENTFLOW_NODE_BRANCH=msk`,
`AGENTFLOW_DEMO_MODE=true`, `AGENTFLOW_SEED_ON_BOOT=true`; secret
`AGENTFLOW_NODE_TOKEN`.
- **Edge spb / ekb:** `AGENTFLOW_NODE_ROLE=edge`,
`AGENTFLOW_NODE_BRANCH=spb|ekb`,
`AGENTFLOW_NODE_CENTER_URL=https://liovina-agentflow-center.hf.space`,
`AGENTFLOW_DEMO_MODE=true`, `AGENTFLOW_SEED_ON_BOOT=true`; secret
`AGENTFLOW_NODE_TOKEN` (same value as the center).

A misconfigured edge (no center URL or no token) fails its boot fast by design.

## Verify live (§12 of the build contract)

```bash
CENTER=https://liovina-agentflow-center.hf.space
SPB=https://liovina-agentflow-edge-spb.hf.space
EKB=https://liovina-agentflow-edge-ekb.hf.space
TOKEN=... # the shared node token, from your secret store — never commit it

# 1. All three healthy
curl -fsS $CENTER/v1/health && curl -fsS $SPB/v1/health && curl -fsS $EKB/v1/health

# 2. Public demo-key cannot push (403 — demo-guard holds)
curl -i -X POST -H "X-API-Key: demo-key" -H "Content-Type: application/json" \
-d '{"origin_branch":"spb","events":[]}' $CENTER/v1/node/events

# 3. Cross-branch view before any live event — silent branches show "waking"
curl -fsS -H "X-API-Key: demo-key" $CENTER/v1/node/branches

# 4. Open an edge (wakes it; its generator emits to the hub), wait, then re-read
# the center view — that branch's live_delta is non-zero and last_seen is set.
curl -fsS $SPB/v1/health
sleep 30
curl -fsS -H "X-API-Key: demo-key" $CENTER/v1/node/branches
```

## Refresh after a repo change

Each Space tracks `main` (`ARG AGENTFLOW_REF=main`). Trigger a **Factory
rebuild** from the Space UI after a merge, or bump `AGENTFLOW_REF` to a tag for a
pinned demo.
76 changes: 76 additions & 0 deletions deploy/hf-space/three-node/center/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: AgentFlow — Center (msk hub)
colorFrom: indigo
colorTo: gray
sdk: docker
app_port: 8000
pinned: false
---

# AgentFlow — Center node (`msk` hub)

The **hub** of the three-node demo (ADR 0012). It runs the serving API in demo
mode and additionally accepts operational events pushed by the regional edge
branches over HTTPS, aggregating a **live cross-branch view**. Built from the
public source at
[github.com/brownjuly2003-code/agentflow](https://github.com/brownjuly2003-code/agentflow),
tracking `main` — the same image as the edges, differentiated only by
environment.

The other two nodes:

- **Edge `spb`** — https://liovina-agentflow-edge-spb.hf.space
- **Edge `ekb`** — https://liovina-agentflow-edge-ekb.hf.space

## What this node adds over the standalone demo

- `GET /v1/node/branches` — the cross-branch summary: per branch a seeded
baseline, the live delta accrued this lifecycle, and a last-seen timestamp. A
branch that has sent nothing shows a `waking` status — open its Space to see
its events flow here.
- `POST /v1/node/events` — token-authenticated ingest for edge→center events
(not the public `demo-key`; internal node-to-node, hidden from `/docs`).

## What this demo shows — and what it does NOT

This is a demo of **branch distribution / event federation**: distinct
geographic branch nodes emit operational events to a hub that aggregates a
cross-branch view. Stated plainly so the artifact never over-claims:

- **Transport:** direct HTTPS `POST` stands in for Kafka → Flink. The event
*semantics* are identical (the same canonical events drive the same journal
and metric freshness); only the transport differs, because Spaces cannot run
Kafka.
- **State:** each node is single-replica with an **embedded** control plane —
the correct single-replica profile, **not** the multi-replica PostgreSQL
scale profile (that is the Kubernetes cutover, verified separately). No
database spans the Spaces.
- **Liveness:** the federated live layer is **ephemeral**. Free Spaces have
non-persistent disk and sleep after 48 h of inactivity; on a restart the hub
re-seeds a deterministic baseline and the live layer resets to zero.

## Environment (set in Space **Settings**, not the image)

| Variable | Value | Kind |
|-------------------------|--------------------------------|----------|
| `AGENTFLOW_NODE_ROLE` | `center` | variable |
| `AGENTFLOW_NODE_BRANCH` | `msk` | variable |
| `AGENTFLOW_NODE_TOKEN` | shared node token | **secret** |
| `AGENTFLOW_DEMO_MODE` | `true` | variable |
| `AGENTFLOW_SEED_ON_BOOT`| `true` | variable |

## Try it

```bash
SPACE=https://liovina-agentflow-center.hf.space

# Liveness
curl -fsS $SPACE/v1/health

# Cross-branch view (public demo key)
curl -fsS -H "X-API-Key: demo-key" $SPACE/v1/node/branches

# Public callers cannot push events — the node token is required (403)
curl -i -X POST -H "X-API-Key: demo-key" -H "Content-Type: application/json" \
-d '{"origin_branch":"spb","events":[]}' $SPACE/v1/node/events
```
65 changes: 65 additions & 0 deletions deploy/hf-space/three-node/edge-ekb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: AgentFlow — Edge (ekb)
colorFrom: gray
colorTo: indigo
sdk: docker
app_port: 8000
pinned: false
---

# AgentFlow — Edge node (`ekb`)

A regional-warehouse **edge** of the three-node demo (ADR 0012). It runs the
serving API in demo mode over its own `ekb` branch slice and, while awake, emits
operational events over HTTPS to the **center hub**, whose cross-branch view
reflects them live. Same image as the hub and the other edge, differentiated
only by environment; built from
[github.com/brownjuly2003-code/agentflow](https://github.com/brownjuly2003-code/agentflow),
tracking `main`.

- **Center hub** — https://liovina-agentflow-center.hf.space (open it to see the
cross-branch view)
- **Edge `spb`** — https://liovina-agentflow-edge-spb.hf.space

## How the federation works

A slow background generator produces the same canonical events the in-process
pipeline makes; each is applied to this edge's own read surface **and** forwarded
to the hub's `POST /v1/node/events`. Push-on-activity is what makes the sleep
choreography self-healing: if the hub is asleep, the first forwarded event wakes
it; a cold hub is tolerated (bounded retries, then drop) so this page stays live
regardless.

## What this demo shows — and what it does NOT

Branch distribution / event federation, **not** horizontal scaling of one node:

- **Transport:** HTTPS `POST` stands in for Kafka → Flink; event semantics are
identical, only the transport differs (Spaces cannot run Kafka).
- **State:** single-replica, embedded control plane — **not** the multi-replica
PostgreSQL scale profile (the Kubernetes cutover, verified separately).
- **Liveness:** ephemeral. Free Spaces sleep after 48 h and have non-persistent
disk; a restart re-seeds a deterministic baseline and resets the live layer.

## Environment (set in Space **Settings**, not the image)

| Variable | Value | Kind |
|----------------------------|-----------------------------------------------|----------|
| `AGENTFLOW_NODE_ROLE` | `edge` | variable |
| `AGENTFLOW_NODE_BRANCH` | `ekb` | variable |
| `AGENTFLOW_NODE_CENTER_URL`| `https://liovina-agentflow-center.hf.space` | variable |
| `AGENTFLOW_NODE_TOKEN` | shared node token (same value as the hub) | **secret** |
| `AGENTFLOW_DEMO_MODE` | `true` | variable |
| `AGENTFLOW_SEED_ON_BOOT` | `true` | variable |

## Try it

```bash
SPACE=https://liovina-agentflow-edge-ekb.hf.space

# Liveness (also wakes the edge, which starts emitting to the hub)
curl -fsS $SPACE/v1/health

# This edge's own read surface (public demo key)
curl -fsS -H "X-API-Key: demo-key" $SPACE/v1/entity/order/ORD-20260404-1001
```
65 changes: 65 additions & 0 deletions deploy/hf-space/three-node/edge-spb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: AgentFlow — Edge (spb)
colorFrom: gray
colorTo: indigo
sdk: docker
app_port: 8000
pinned: false
---

# AgentFlow — Edge node (`spb`)

A regional-warehouse **edge** of the three-node demo (ADR 0012). It runs the
serving API in demo mode over its own `spb` branch slice and, while awake, emits
operational events over HTTPS to the **center hub**, whose cross-branch view
reflects them live. Same image as the hub and the other edge, differentiated
only by environment; built from
[github.com/brownjuly2003-code/agentflow](https://github.com/brownjuly2003-code/agentflow),
tracking `main`.

- **Center hub** — https://liovina-agentflow-center.hf.space (open it to see the
cross-branch view)
- **Edge `ekb`** — https://liovina-agentflow-edge-ekb.hf.space

## How the federation works

A slow background generator produces the same canonical events the in-process
pipeline makes; each is applied to this edge's own read surface **and** forwarded
to the hub's `POST /v1/node/events`. Push-on-activity is what makes the sleep
choreography self-healing: if the hub is asleep, the first forwarded event wakes
it; a cold hub is tolerated (bounded retries, then drop) so this page stays live
regardless.

## What this demo shows — and what it does NOT

Branch distribution / event federation, **not** horizontal scaling of one node:

- **Transport:** HTTPS `POST` stands in for Kafka → Flink; event semantics are
identical, only the transport differs (Spaces cannot run Kafka).
- **State:** single-replica, embedded control plane — **not** the multi-replica
PostgreSQL scale profile (the Kubernetes cutover, verified separately).
- **Liveness:** ephemeral. Free Spaces sleep after 48 h and have non-persistent
disk; a restart re-seeds a deterministic baseline and resets the live layer.

## Environment (set in Space **Settings**, not the image)

| Variable | Value | Kind |
|----------------------------|-----------------------------------------------|----------|
| `AGENTFLOW_NODE_ROLE` | `edge` | variable |
| `AGENTFLOW_NODE_BRANCH` | `spb` | variable |
| `AGENTFLOW_NODE_CENTER_URL`| `https://liovina-agentflow-center.hf.space` | variable |
| `AGENTFLOW_NODE_TOKEN` | shared node token (same value as the hub) | **secret** |
| `AGENTFLOW_DEMO_MODE` | `true` | variable |
| `AGENTFLOW_SEED_ON_BOOT` | `true` | variable |

## Try it

```bash
SPACE=https://liovina-agentflow-edge-spb.hf.space

# Liveness (also wakes the edge, which starts emitting to the hub)
curl -fsS $SPACE/v1/health

# This edge's own read surface (public demo key)
curl -fsS -H "X-API-Key: demo-key" $SPACE/v1/entity/order/ORD-20260404-1001
```
7 changes: 5 additions & 2 deletions docs/three-node-demo-topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,11 @@ default** behind a flag - the core design is push-only (ADR 0012 Decision 5).

## 11. Deploy runbook skeleton (owner gate)

Mirrors `deploy/hf-space/DEPLOY.md` x3. `HF_TOKEN` from `D:/VacancyRadar/.env` - never
printed. For each of `agentflow-center`, `agentflow-edge-spb`, `agentflow-edge-ekb`:
The concrete runbook and per-role Space READMEs live in
`deploy/hf-space/three-node/` (`DEPLOY.md` + `center/`, `edge-spb/`, `edge-ekb/`
READMEs; the shared image is the existing `deploy/hf-space/Dockerfile`).
`HF_TOKEN` from `D:/VacancyRadar/.env` - never printed. For each of
`agentflow-center`, `agentflow-edge-spb`, `agentflow-edge-ekb`:

```bash
# 1. Create (one-time)
Expand Down
Loading