Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
b9888c1
chore(storefront): name the static site for what it actually serves
bussyjd Jul 15, 2026
ac99ef5
refactor(storefront): embed the offer landing page like every other t…
bussyjd Jul 15, 2026
06d13b4
docs(x402): fix a token-sync contract that described a mirror that is…
bussyjd Jul 15, 2026
342c0aa
feat(storefront): built-in browser chat widget for agent-type offers
bussyjd Jul 14, 2026
6655398
fix(storefront): cache-bust the chat widget vendor import
bussyjd Jul 14, 2026
a7441e7
fix(storefront): poll the session balance so external funding unlocks…
bussyjd Jul 14, 2026
7e9181c
fix(hermes): allow browser origins on the agent API server
bussyjd Jul 14, 2026
7997df3
fix(storefront): fold chat widget assets into the catalog content hash
bussyjd Jul 14, 2026
d37321d
wip(chat): compact composer, per-offer theming, cache headers, verifi…
bussyjd Jul 15, 2026
166a4f7
wip(chat): one-click connect+sign-in, drop reveal-key
bussyjd Jul 15, 2026
66a77f6
wip(chat): landing chat iframe bleeds to the card edges
bussyjd Jul 15, 2026
f50fe7a
wip(chat): chrome-less embed — widget hides its header when framed
bussyjd Jul 15, 2026
c50aa86
wip(chat): uncramp the composer
bussyjd Jul 15, 2026
0fa4e85
fix(storefront): API docs links on their own line in the service card
bussyjd Jul 15, 2026
a585aeb
wip(chat): stream replies + per-payment balance ceiling
bussyjd Jul 15, 2026
49b43e4
fix(hermes): edge-redirect dashboard root to password-login
bussyjd Jul 15, 2026
c6b9802
docs: align install quickstart with v0.13.0 product behavior
bussyjd Jul 15, 2026
a0a8c6e
merge: #753 chore(storefront) page-surface naming
bussyjd Jul 15, 2026
f520c38
merge: #754 fix(hermes) dashboard password-login redirect
bussyjd Jul 15, 2026
919502e
merge: #755 docs hackathon quickstart accuracy
bussyjd Jul 15, 2026
4726dcf
fix(serviceoffer): namespace-disambiguate ReferenceGrant names in x402
bussyjd Jul 15, 2026
27e784d
fix(serviceoffer): require 2xx for UpstreamHealthy probes
bussyjd Jul 15, 2026
367c0b8
fix(serviceoffer): split inFlightReq and rateLimit into two Traefik M…
bussyjd Jul 15, 2026
ae5ccba
fix(x402): advertise public path and https in 402 resource.URL
bussyjd Jul 15, 2026
84dcbf8
fix(sell): enable registration on offers after CLI ERC-8004 register
bussyjd Jul 15, 2026
23e01b1
merge: fix/referencegrant-namespace-collision into integration/v0.14.…
bussyjd Jul 15, 2026
e5eebc9
merge: fix/upstream-healthy-2xx into integration/v0.14.0-rc0
bussyjd Jul 15, 2026
3fe2ae8
merge: fix/limits-middleware-split into integration/v0.14.0-rc0
bussyjd Jul 15, 2026
e13d8d0
merge: fix/x402-public-resource-url into integration/v0.14.0-rc0
bussyjd Jul 15, 2026
3e7218a
merge: fix/erc8004-register-enable-offers into integration/v0.14.0-rc0
bussyjd Jul 15, 2026
518e467
Delete docs/guides/docs-obol-org-hackathon-gaps.md
bussyjd Jul 16, 2026
019fe52
feat(chat): withdraw session funds + refuse contract wallets
bussyjd Jul 16, 2026
aa22f9c
feat(chat): EIP-4361 sign-in message — wallet-enforced domain binding
bussyjd Jul 16, 2026
a9361fd
feat(chat): session-strip icon row
bussyjd Jul 16, 2026
fe6d4ca
merge: #755 delete dud docs-obol-org-hackathon-gaps.md
bussyjd Jul 16, 2026
46cbcf3
merge: #752 feat/agent-chat-widget into integration/v0.14.0-rc0
bussyjd Jul 16, 2026
77a6e0d
merge: #762 feat/chat-siwe-session into integration/v0.14.0-rc0
bussyjd Jul 16, 2026
4c8bf7b
fix(sell): stop defaulting agent offer description to the internal ob…
bussyjd Jul 16, 2026
63a25b9
fix(x402scan): detect testnet-only origins and surface per-endpoint 4…
bussyjd Jul 16, 2026
d417e66
fix(serviceoffer): gate RoutePublished on Traefik acceptance, sweep l…
bussyjd Jul 16, 2026
63e1410
fix(sell): registration-hygiene trio — origin validation, nonce pinni…
bussyjd Jul 16, 2026
5fc755c
fix(serviceoffer): scope ERC-8004 agentId to the offer's own chain
bussyjd Jul 16, 2026
23ae63a
fix(tunnel): tear down storefront route when every hostname is offer-…
bussyjd Jul 16, 2026
6e6bd2b
fix(serviceoffer): make ReferenceGrant name injective over (namespace…
bussyjd Jul 16, 2026
67623ef
docs(testing): proactive bug-finding harness — invariants, canary, ag…
bussyjd Jul 16, 2026
0f60428
fix(x402): validate price input and fail closed on malformed on-chain…
bussyjd Jul 16, 2026
4012b5f
docs(testing): record whole-product-surface audit (2026-07-16, 21 fin…
bussyjd Jul 16, 2026
854cb32
fix(obolup): fail closed on checksum-verification failure
bussyjd Jul 16, 2026
df95849
fix(agent): validate --id as a DNS label to stop /etc/hosts injection
bussyjd Jul 16, 2026
75b3e68
fix(stack): gate backend-switch destroy in init --force on live-servi…
bussyjd Jul 16, 2026
93fac4b
fix(openclaw): escape untrusted import fields before writing overlay …
bussyjd Jul 16, 2026
08accc8
feat(network): durable eRPC operator overlays that survive stack up
bussyjd Jul 16, 2026
9251cb3
fix(discovery): full upstream OpenAPI + per-origin agent-registration
bussyjd Jul 16, 2026
d6cf4f8
refactor(network): erpc set|status|reset — align verbs with stack pat…
bussyjd Jul 16, 2026
713c422
fix(network): avoid CodeQL allocation-size-overflow in eRPC merge
bussyjd Jul 16, 2026
bf4dcba
merge: #770 chore/proactive-bug-finding into integration/v0.14.0-rc0 …
bussyjd Jul 16, 2026
b3750be
merge: #776 fix/obolup-checksum-failclosed into integration/v0.14.0-r…
bussyjd Jul 16, 2026
15e4bab
merge: #768 fix/agent-description-leak into integration/v0.14.0-rc0 —…
bussyjd Jul 16, 2026
5eafb5e
merge: #773 fix/stack-init-guard into integration/v0.14.0-rc0 — fix(s…
bussyjd Jul 16, 2026
0859b86
merge: #774 fix/agent-id-validation into integration/v0.14.0-rc0 — fi…
bussyjd Jul 16, 2026
f1347c0
merge: #775 fix/openclaw-yaml-injection into integration/v0.14.0-rc0 …
bussyjd Jul 16, 2026
ca52a6f
merge: #765 fix/sell-register-hygiene into integration/v0.14.0-rc0 — …
bussyjd Jul 16, 2026
b0cd288
merge: #766 fix/tunnel-storefront-teardown into integration/v0.14.0-r…
bussyjd Jul 16, 2026
517de47
merge: #769 fix/x402scan-network-diagnostics into integration/v0.14.0…
bussyjd Jul 16, 2026
7577b11
merge: #771 fix/price-validation into integration/v0.14.0-rc0 — fix(x…
bussyjd Jul 16, 2026
52263e4
merge: #764 fix/erc8004-chain-scoped-agentid into integration/v0.14.0…
bussyjd Jul 16, 2026
7851a74
merge: #767 fix/serviceoffer-route-acceptance into integration/v0.14.…
bussyjd Jul 16, 2026
8e7d369
merge: #777 feat/discovery-host-openapi-registration into integration…
bussyjd Jul 16, 2026
060008e
merge: #772 feat/erpc-operator-overlay into integration/v0.14.0-rc0 —…
bussyjd Jul 16, 2026
7540ce6
fix(flows): probe LiteLLM readiness in flow-06
bussyjd Jul 16, 2026
8a33b7d
fix(serviceoffer): probe Hermes API /health for agent offers
bussyjd Jul 16, 2026
c25699c
fix(sell): default LiteLLM healthPath to /health/readiness
bussyjd Jul 16, 2026
b00fa5d
chore: remove internal audit tooling/docs from the production repo
bussyjd Jul 16, 2026
4daa93c
fix(x402): fail closed on zero/sub-atomic priced routes, preserve Ori…
bussyjd Jul 16, 2026
eee0bc2
fix(openclaw): yaml-escape Ollama model names in overlay generation
bussyjd Jul 16, 2026
99af155
fix(chat-widget): harden /chat against clickjacking, stale-bundle dri…
bussyjd Jul 16, 2026
906ebf3
fix(sell): default ollama health-path to / and harden registration edges
bussyjd Jul 16, 2026
0050731
fix(network): validate + preserve eRPC overlay entries on set/reset
bussyjd Jul 16, 2026
f8e8e66
fix(discovery): harden upstream-OpenAPI discovery (size, cache, SSRF,…
bussyjd Jul 16, 2026
f570810
fix: five independent nits (shared-slice mutation, storefront no-op, …
bussyjd Jul 16, 2026
a9877ec
fix(serviceoffercontroller): gate httpRouteAccepted on observedGenera…
bussyjd Jul 16, 2026
2163433
Merge pull request #778 from ObolNetwork/chore/wipe-internal-tooling
bussyjd Jul 16, 2026
aabd024
Merge pull request #779 from ObolNetwork/fix/discovery-openapi-hardening
bussyjd Jul 16, 2026
37ac065
Merge pull request #780 from ObolNetwork/fix/inference-ollama-healthpath
bussyjd Jul 16, 2026
1f5e0b8
Merge pull request #781 from ObolNetwork/fix/route-acceptance-observe…
bussyjd Jul 16, 2026
221e980
Merge pull request #782 from ObolNetwork/fix/erpc-overlay-hardening
bussyjd Jul 16, 2026
6d17999
Merge pull request #783 from ObolNetwork/fix/openclaw-ollama-yaml
bussyjd Jul 16, 2026
9785496
Merge pull request #784 from ObolNetwork/fix/x402-zero-price
bussyjd Jul 16, 2026
b365dfd
Merge pull request #785 from ObolNetwork/fix/chat-widget-hardening
bussyjd Jul 16, 2026
c2d5c58
Merge pull request #786 from ObolNetwork/fix/misc-nits
bussyjd Jul 16, 2026
78c78a8
revert(storefront): carve chat widget out of v0.14.0-rc0
bussyjd Jul 17, 2026
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
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ obol sell delete ollama-gated -n llm

## RPC Gateway

`obol network add|remove|status` manages remote RPCs via eRPC ConfigMap. Default: read-only (blocks `eth_sendRawTransaction`, `eth_sendTransaction`). `--allow-writes` flips readOnly → route `eth_sendRawTransaction` to the user-chosen upstream via eRPC per-method selection policy. `--endpoint` adds a custom RPC directly (skips ChainList). Key functions in `internal/network/rpc.go`: `AddPublicRPCs()` (ChainList), `AddCustomRPC()`, `ListRPCNetworks()`. Record-on-write: add/remove update `$CONFIG_DIR/rpc/recorded-upstreams.yaml` (`internal/network/record.go`); `obol stack up` replays via `ReconcileRecordedRPCs()` so remote RPCs survive cluster recreation. Local-node upstreams are NOT recorded (`network sync` re-registers them).
`obol network add|remove|status` manages remote RPCs via eRPC ConfigMap. Default: read-only (blocks `eth_sendRawTransaction`, `eth_sendTransaction`). `--allow-writes` flips readOnly → route `eth_sendRawTransaction` to the user-chosen upstream via eRPC per-method selection policy. `--endpoint` adds a custom RPC directly (skips ChainList). Key functions in `internal/network/rpc.go`: `AddPublicRPCs()` (ChainList), `AddCustomRPC()`, `ListRPCNetworks()`. Record-on-write: add/remove update `$CONFIG_DIR/rpc/recorded-upstreams.yaml` (`internal/network/record.go`); `obol stack up` replays via `ReconcileRecordedRPCs()` so remote RPCs survive cluster recreation. Local-node upstreams are NOT recorded (`network sync` re-registers them). **Durable eRPC operator config** (so local baskets are not lost on stack up): `obol network erpc set|status|reset` persists `$CONFIG_DIR/rpc/erpc-overlay.yaml` (merge-by-id into the live ConfigMap; not a full replace) and re-applies after recorded remotes on stack up via `ReconcileERPCOverlay()` (`internal/network/overlay.go`, #763). Example: `examples/erpc-overlay-hyperevm.yaml`.

## Network Management

Expand All @@ -242,7 +242,7 @@ Two-stage templating: `values.yaml.gotmpl` annotated with `@enum`/`@default`/`@d
| Command | Action |
|---------|--------|
| `obol stack init` | Generate cluster ID (petname), resolve absolute paths, write k3d.yaml, copy infrastructure |
| `obol stack up` | `k3d cluster create`, export kubeconfig, k3s auto-applies manifests, auto-configures LiteLLM with Ollama models (preserves Ollama modified-time order; `:cloud` aliases demoted behind local chat models; embedding-only models last; warns + suggests `ollama pull qwen3.5:4b` when empty or all-`:cloud`), re-imposes recorded model config (`model.ReconcileRecorded` — operator's `obol model` choices win over auto-detect), deploys default Hermes agent, applies agent capabilities, starts persistent Cloudflare tunnel, then replays recorded state: RPC upstreams (`network.ReconcileRecordedRPCs`) → Agent CRs (`agentcrd.ResumeAll`) → sell offers (`resumeSellOffers`; agent-type offers ride the sell-http store). Guard: `cmd/obol/stackup_resume_guard_test.go` |
| `obol stack up` | `k3d cluster create`, export kubeconfig, k3s auto-applies manifests, auto-configures LiteLLM with Ollama models (preserves Ollama modified-time order; `:cloud` aliases demoted behind local chat models; embedding-only models last; warns + suggests `ollama pull qwen3.5:4b` when empty or all-`:cloud`), re-imposes recorded model config (`model.ReconcileRecorded` — operator's `obol model` choices win over auto-detect), deploys default Hermes agent, applies agent capabilities, starts persistent Cloudflare tunnel, then replays recorded state: RPC upstreams (`network.ReconcileRecordedRPCs`) → eRPC operator overlay (`network.ReconcileERPCOverlay`) → Agent CRs (`agentcrd.ResumeAll`) → sell offers (`resumeSellOffers`; agent-type offers ride the sell-http store). Guard: `cmd/obol/stackup_resume_guard_test.go` |
| `obol stack down` | `k3d cluster stop` (delete fallback; preserves config + data) |
| `obol stack purge [-f]` | Delete config; `-f` also deletes root-owned PVCs; `-f` offers a full `stack export` first (fallback: OpenClaw wallet prompt) |
| `obol stack export` | Full backup archive: config dir (minus kubeconfig/defaults), agent data dirs (brains + keystores, deployments quiesced for consistency), encrypted wallet backups, etcd-drift resources (Agent CRs, ServiceOffers, LiteLLM/eRPC CMs). `internal/stackbackup/` |
Expand Down
64 changes: 60 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,33 @@ Built on [Kubernetes](https://kubernetes.io) with [Helm](https://helm.sh/) for p
- **Linux**: [Docker Engine installation guide](https://docs.docker.com/engine/install/)
- **macOS/Windows**: [Docker Desktop](https://docs.docker.com/desktop/)

For local models, install [Ollama](https://ollama.com) and pull at least one chat-capable model (e.g. `ollama pull qwen3:8b`). Skip this if you'll use a cloud provider (see [Models](#models)).
For local models, install [Ollama](https://ollama.com) and pull at least one chat-capable model (e.g. `ollama pull qwen3.5:4b`). The installer may offer to install Ollama; **if you decline and do not configure a cloud model**, `obol stack up` **skips the default Hermes agent** until you run `obol model setup` and then `obol agent init`. Prefer a cloud provider anytime with `obol model setup` (see [Models](#models)).

### Install

```bash
bash <(curl -fsSL https://stack.obol.org)
```

The installer sets up the `obol` CLI and all dependencies (`kubectl`, `helm`, `k3d`, `helmfile`, `k9s`) into `~/.local/bin/`, verifies release checksums, configures your PATH, and offers to start the cluster.
Pin a release (use the current tag from the [releases page](https://github.com/ObolNetwork/obol-stack/releases), not an ancient example):

```bash
OBOL_RELEASE=v0.13.0 bash <(curl -fsSL https://stack.obol.org)
```

The installer sets up the `obol` CLI and all dependencies (`kubectl`, `helm`, `k3d`, `helmfile`, `k9s`) into `~/.local/bin/`, verifies release checksums, configures your PATH, tries to add `obol.stack` to `/etc/hosts`, and offers to start the cluster.

If `/etc/hosts` cannot be updated (no sudo / you cancel), the install still finishes — add the host, then start the stack yourself:

```bash
echo "127.0.0.1 obol.stack" | sudo tee -a /etc/hosts
# After stack up, agent hostnames are also managed (obol-agent.obol.stack, …)
obol stack init
obol stack up
obol agent init # if the default agent was skipped (no model yet)
```

Non-interactive / CI: set `OBOL_NONINTERACTIVE=true` so hosts updates do not prompt for a sudo password (they fail fast if credentials are not already cached). Combine with a pre-written `/etc/hosts` entry or run `sudo -v` first.

Verify:

Expand All @@ -65,7 +83,33 @@ obol agent list
obol agent auth obol-agent
```

`obol stack up` provisions the cluster, auto-detects your local Ollama models into the LiteLLM gateway, deploys the default Hermes agent (with its own wallet behind a remote signer), and starts a Cloudflare quick-tunnel. From here you can chat with your agent locally at `http://obol.stack:8080` — or go straight to selling.
`obol stack up` provisions the cluster, auto-detects host Ollama models into the **LiteLLM** gateway (when present), and deploys the default **Hermes** agent with its own wallet behind a remote signer. The Cloudflare tunnel stays **dormant** until the first sell workflow (or `obol tunnel restart` / `obol tunnel setup`).

### Local UI URL (Host header)

Open the frontend at:

```text
http://obol.stack:8080
```

Use **`obol.stack`**, not `localhost`. Traefik routes the frontend (and eRPC) only for `Host: obol.stack`. `http://localhost:8080` returns **404** even when the stack is healthy.

- Prefer `:8080` on macOS when port 80 is unavailable (or after editing `k3d.yaml` to drop privileged 80/443 binds).
- If port 80 is mapped, `http://obol.stack/` works too.

Hermes dashboard (root edge-redirects to the password form):

```text
http://obol-agent.obol.stack
```

(or with `:8080` when that is your ingress)

- **Username:** `obol`
- **Password:** the agent API token from `obol agent auth obol-agent`

Details: [Hermes dashboard login](docs/guides/hermes-dashboard-login.md).

## Sell: Your First Paid Service

Expand Down Expand Up @@ -406,7 +450,19 @@ Edit `~/.config/obol/k3d.yaml`, remove the `80:80` and `443:443` port entries (k
obol stack down && obol stack up
```

Access at http://obol.stack:8080 instead.
Access at http://obol.stack:8080 instead (still with the `obol.stack` host — not `localhost`).

#### `/etc/hosts` / sudo prompts

`obol stack up` also tries to refresh managed hostnames (`obol.stack`, `obol-agent.obol.stack`, …). A failed hosts write is a **warning**, not a hard stop — the cluster still comes up. Fix hosts, then re-run `obol stack up` or `obol agent sync` so agent hostnames are registered.

```bash
# Minimal manual entry
echo "127.0.0.1 obol.stack" | sudo tee -a /etc/hosts

# Skip interactive sudo during automation (must already have NOPASSWD or a cached timestamp)
OBOL_NONINTERACTIVE=true obol stack up
```

#### Monetize Flow Preflight

Expand Down
2 changes: 1 addition & 1 deletion cmd/obol/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func bootstrapCommand(cfg *config.Config) *cli.Command {

// Step 1: Initialize stack
backendName := stack.DetectExistingBackend(cfg)
if err := stack.Init(cfg, u, false, backendName); err != nil {
if err := stack.Init(cfg, u, false, backendName, false); err != nil {
if !strings.Contains(err.Error(), "already exists") {
return fmt.Errorf("bootstrap init failed: %w", err)
}
Expand Down
12 changes: 11 additions & 1 deletion cmd/obol/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,14 @@ GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
Usage: "Cluster backend: k3d (Docker-based) or k3s (bare-metal)",
Sources: cli.EnvVars("OBOL_BACKEND"),
},
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"y"},
Usage: "Skip the live-services confirmation prompt when --force switches backends (required in non-interactive shells when offers are running)",
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return stack.Init(cfg, getUI(cmd), cmd.Bool("force"), cmd.String("backend"))
return stack.Init(cfg, getUI(cmd), cmd.Bool("force"), cmd.String("backend"), cmd.Bool("yes"))
},
},
{
Expand All @@ -212,6 +217,11 @@ GLOBAL OPTIONS:{{template "visibleFlagTemplate" .}}{{end}}
// Replay recorded remote RPC upstreams into the
// (possibly fresh) eRPC ConfigMap. Best-effort.
network.ReconcileRecordedRPCs(cfg, u)
// Re-apply durable multi-upstream eRPC operator
// overlays (baskets/scoring/rate-limits) AFTER
// simple recorded remotes. Best-effort.
// See ObolNetwork/obol-stack#763.
network.ReconcileERPCOverlay(cfg, u)
// Re-apply recorded Agent CRs BEFORE sell offers:
// agent-backed ServiceOffers resolve agent.ref and
// would dangle without their Agent. Best-effort.
Expand Down
87 changes: 87 additions & 0 deletions cmd/obol/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func networkCommand(cfg *config.Config) *cli.Command {
networkAddCommand(cfg),
networkRemoveCommand(cfg),
networkStatusCommand(cfg),
networkERPCCommand(cfg),
},
}
}
Expand Down Expand Up @@ -534,6 +535,91 @@ func networkStatusCommand(cfg *config.Config) *cli.Command {
}
}

// network erpc — durable operator eRPC config that survives stack up (#763).
// Verbs match sell-info set/reset (host-side intent) + status.
// ---------------------------------------------------------------------------

func networkERPCCommand(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "erpc",
Usage: "Manage durable operator eRPC config (host-side; re-applied on stack up so local baskets are not lost)",
Commands: []*cli.Command{
{
Name: "set",
Usage: "Set durable eRPC config from a YAML file and merge it into the live ConfigMap",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Usage: "YAML fragment: networks, upstreams, rateLimiters, cachePoliciesAdd (saved to $CONFIG_DIR/rpc/erpc-overlay.yaml)",
Required: true,
},
},
Action: func(_ context.Context, cmd *cli.Command) error {
return network.SetERPC(cfg, getUI(cmd), cmd.String("file"))
},
},
{
Name: "status",
Usage: "Show the durable eRPC config on disk",
Action: func(_ context.Context, cmd *cli.Command) error {
u := getUI(cmd)
st, err := network.StatusERPC(cfg)
if err != nil {
return err
}
u.Printf("eRPC operator config\n")
u.Printf("====================\n\n")
u.Printf("Path: %s\n", st.Path)
if !st.Present {
u.Info("Status: not set")
u.Info("Set with: obol network erpc set -f <file.yaml>")
return nil
}
u.Printf("Status: set (v%d, hash %s)\n", st.Version, st.ContentHash)
switch st.ClusterSync {
case "":
u.Info("Cluster: unknown (could not reach cluster to check for drift)")
case network.ERPCSyncInSync:
u.Info("Cluster: in-sync")
case network.ERPCSyncNotApplied:
u.Warn("Cluster: not-applied (overlay is on disk but not on the live ConfigMap — run `obol network erpc set -f <file>` or `obol stack up`)")
default:
u.Warnf("Cluster: %s (live ConfigMap does not match the on-disk overlay — re-run `obol network erpc set -f <file>`)", st.ClusterSync)
}
u.Printf("Networks (%d):\n", st.NetworkCount)
for _, k := range st.NetworkKeys {
u.Printf(" - %s\n", k)
}
if st.NetworkCount == 0 {
u.Info(" (none)")
}
u.Printf("Upstreams (%d):\n", st.UpstreamCount)
for _, id := range st.UpstreamIDs {
u.Printf(" - %s\n", id)
}
if st.UpstreamCount == 0 {
u.Info(" (none)")
}
u.Printf("Rate-limit budgets: %d\n", st.BudgetCount)
u.Printf("Cache policies to add: %d\n", st.CachePolicyAdd)
return nil
},
},
{
Name: "reset",
Usage: "Reset durable eRPC config (strip from live ConfigMap and delete host file)",
Action: func(_ context.Context, cmd *cli.Command) error {
return network.ResetERPC(cfg, getUI(cmd))
},
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return cli.ShowSubcommandHelp(cmd)
},
}
}

// chainIDToName returns a human-readable name for a chain ID.
func chainIDToName(chainID int) string {
names := map[int]string{
Expand All @@ -550,6 +636,7 @@ func chainIDToName(chainID int) string {
43114: "Avalanche",
59144: "Linea",
84532: "Base Sepolia",
999: "HyperEVM",
534352: "Scroll",
560048: "Hoodi",
11155111: "Sepolia",
Expand Down
Loading
Loading