Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f8ec56d
feat: monetize-guide skill + sell probe command (#286)
bussyjd Mar 21, 2026
32c0065
fix: approximate per-hour pricing to per-request for x402 gating
bussyjd Mar 12, 2026
b67bdf9
feat: add reth-based ERC-8004 indexer (from #269)
bussyjd Mar 12, 2026
57e5517
fix: address PR 265 review findings — security, correctness, test cov…
bussyjd Mar 18, 2026
e2b37b4
fix(m1): add sell probe command and flow validation scripts
bussyjd Mar 23, 2026
5e9149e
fix(m2): CRD skills/domains fields + coordinator x402 V1 parsing
bussyjd Mar 23, 2026
dbec6cf
fix(oasf): align skill/domain taxonomy with official OASF schema
bussyjd Mar 23, 2026
c7008db
fix(oasf): use real schema paths — devops_mlops/model_versioning
bussyjd Mar 23, 2026
dd7b75e
fix: stop stripping /v1 from custom endpoint URLs
bussyjd Mar 24, 2026
723e3a3
docs: add LAN resource selling path to monetize-guide
bussyjd Mar 24, 2026
0b1d58c
fix: restore tunnel open/close and host-service logic dropped during …
bussyjd Mar 24, 2026
d81316f
security: pin LiteLLM image to v1.82.3 — supply chain compromise
bussyjd Mar 24, 2026
ac89757
fix: restore /skill.md publishing dropped during cherry-pick
bussyjd Mar 24, 2026
0be985a
test: add tunnel lifecycle test coverage
bussyjd Mar 24, 2026
3a1ca40
chore: add golangci-lint v2 config with gofumpt formatting (#290)
bussyjd Mar 29, 2026
24084de
Merge remote-tracking branch 'origin/main' into feat/monetize-path
bussyjd Mar 29, 2026
f3a11dd
Merge remote-tracking branch 'origin/feat/monetize-path' into feat/mo…
bussyjd Mar 29, 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
17 changes: 17 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path // .tool_response.filePath' | { read -r f; case \"$f\" in *.go) golangci-lint fmt \"$f\" ;; esac; } 2>/dev/null || true",
"timeout": 30,
"statusMessage": "Formatting Go file..."
}
]
}
]
}
}
189 changes: 189 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
version: "2"
run:
go: "1.25"
linters:
default: all
disable:
- containedctx
- contextcheck
- cyclop
- depguard
- err113
- exhaustruct
- forbidigo # CLI uses fmt.Print* for user output
- forcetypeassert
- funlen
- funcorder
- gochecknoglobals
- gocognit
- gocyclo
- godoclint # don't require doc comments on all exports
- godot
- godox
- gomoddirectives
- inamedparam
- interfacebloat
- intrange
- ireturn
- lll
- maintidx
- mnd
- musttag
- nestif
- nlreturn # stylistic, too noisy
- noctx # too much to retrofit; revisit later
- noinlineerr
- nonamedreturns
- paralleltest
- prealloc
- recvcheck
- tagliatelle
- testpackage # internal test packages are fine here
- varnamelen
- wrapcheck # obol-stack has no app/errors wrapper package
- wsl
# TODO: re-enable incrementally as issues are fixed
# All linters re-enabled
settings:
dupl:
threshold: 400
exhaustive:
default-signifies-exhaustive: true
gocritic:
disabled-checks:
- ifElseChain
gosec:
excludes:
- G115
- G204 # CLI tool intentionally runs subprocesses (k3d, helm, kubectl)
- G101 # false positives from variable names containing "key", "token", "secret"
govet:
disable:
- fieldalignment
- shadow
enable-all: true
revive:
severity: warning
enable-all-rules: true
rules:
- name: add-constant
disabled: true
- name: argument-limit
disabled: true
- name: banned-characters
disabled: true
- name: cognitive-complexity
disabled: true
- name: comment-spacings
disabled: true
- name: cyclomatic
disabled: true
- name: file-header
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: line-length-limit
disabled: true
- name: max-public-structs
disabled: true
- name: range-val-address
disabled: true
- name: unhandled-error
disabled: true
- name: unused-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: use-fmt-print
disabled: true
- name: unnecessary-format
disabled: true
- name: enforce-switch-style
disabled: true
- name: unsecure-url-scheme
disabled: true
- name: deep-exit
disabled: true
# TODO: re-enable as issues are fixed
- name: confusing-results
disabled: true
- name: early-return
disabled: true
- name: exported
disabled: true
- name: identical-branches
disabled: true
- name: identical-switch-branches
disabled: true
- name: if-return
disabled: true
- name: import-alias-naming
disabled: true
- name: superfluous-else
disabled: true
- name: unchecked-type-assertion
disabled: true
- name: unexported-return
disabled: true
- name: use-slices-sort
disabled: true
- name: var-naming
disabled: true
staticcheck:
checks:
- -SA1019
- all
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- bodyclose
- errcheck
- errchkjson
- gosec
- revive
- unparam
- goconst
path: (.+)_test\.go
- linters:
- gosec
path: internal/testutil/
- path: (.+)\.go$
text: error returned from interface method should be wrapped
- path: (.+)\.go$
text: "defer: prefer not to defer chains of function calls"
- path: (.+)\.go$
text: avoid control coupling
- path: (.+)\.go$
text: shadows an import name
- path: (.+)\.go$
text: confusing-naming
- path: (.+)\.go$
text: nested-structs
- path: (.+)\.go$
text: 'shadow: declaration of "err" shadows declaration'
- linters:
- cyclop
path: (.+)_test\.go
issues:
max-issues-per-linter: 0
max-same-issues: 0
fix: false
formatters:
enable:
- gofmt
- gofumpt
- goimports
settings:
gci:
sections:
- prefix(github.com/ObolNetwork/obol-stack)
exclusions:
generated: lax
53 changes: 12 additions & 41 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Obol Stack: framework for AI agents to run decentralised infrastructure locally. k3d cluster with OpenClaw AI agent, blockchain networks, payment-gated inference (x402), Cloudflare tunnels. CLI: `github.com/urfave/cli/v3`.

## Conventions

- **Commits**: Conventional commits — `feat:`, `fix:`, `docs:`, `test:`, `chore:`, `security:` with optional scope
- **Branches**: `feat/`, `fix/`, `research/`, `codex/` prefixes
- **Detailed architecture reference**: `@.claude/skills/obol-stack-dev/SKILL.md` (invoke with `/obol-stack-dev`)

## Build, Test, Run

```bash
Expand Down Expand Up @@ -113,14 +121,7 @@ k3d: 1 server, ports 80:80 + 8080:80 + 443:443 + 8443:443, `rancher/k3s:v1.35.1-

## Standalone Inference Gateway

`obol sell inference` — standalone OpenAI-compatible HTTP gateway with x402 payment gating, for bare metal / Secure Enclave. `--vm` flag runs Ollama in Apple Containerization Linux VM.

| Component | File | Role |
|-----------|------|------|
| `Gateway` | `internal/inference/gateway.go` | HTTP server, x402 middleware, Ollama proxy |
| `ContainerManager` | `internal/inference/container.go` | Apple Containerization VM lifecycle |
| `Store` | `internal/inference/store.go` | Deployment config persistence |
| `Key` interface | `internal/enclave/enclave.go` | Secure Enclave signing (`enclave_darwin.go`: CGo/Security.framework, `enclave_stub.go`: fallback) |
`obol sell inference` — standalone OpenAI-compatible HTTP gateway with x402 payment gating, for bare metal / Secure Enclave. `--vm` flag runs Ollama in Apple Containerization Linux VM. Key code: `internal/inference/` (gateway, container, store) and `internal/enclave/` (Secure Enclave signing via CGo/Security.framework on Darwin, stub fallback elsewhere).

## OpenClaw & Skills

Expand All @@ -132,15 +133,7 @@ Skills = SKILL.md + optional scripts/references, embedded in `obol` binary (`int

## Buyer Sidecar

`x402-buyer` — lean Go sidecar for buy-side x402 payments using pre-signed ERC-3009 authorizations. It runs as a second container in the `litellm` Deployment, not as a separate Service. Agent `buy.py` commands mutate only buyer ConfigMaps; LiteLLM keeps one static public namespace `paid/<remote-model>`. The sidecar exposes `/status`, `/healthz`, and `/metrics`; metrics are scraped via `PodMonitor`. Zero signer access, bounded spending (max loss = N × price).

| Component | File |
|-----------|------|
| Sidecar binary | `cmd/x402-buyer/main.go` |
| PreSignedSigner | `internal/x402/buyer/signer.go` |
| Reverse proxy | `internal/x402/buyer/proxy.go` |
| Config/types | `internal/x402/buyer/config.go` |
| Buy skill | `internal/embed/skills/buy-inference/` |
`x402-buyer` — lean Go sidecar for buy-side x402 payments using pre-signed ERC-3009 authorizations. It runs as a second container in the `litellm` Deployment, not as a separate Service. Agent `buy.py` commands mutate only buyer ConfigMaps; LiteLLM keeps one static public namespace `paid/<remote-model>`. The sidecar exposes `/status`, `/healthz`, and `/metrics`; metrics are scraped via `PodMonitor`. Zero signer access, bounded spending (max loss = N × price). Key code: `cmd/x402-buyer/` and `internal/x402/buyer/`.

## Development Constraints

Expand Down Expand Up @@ -183,31 +176,9 @@ The Cloudflare tunnel exposes the cluster to the public internet. Only x402-gate
- `/skill.md` — machine-readable service catalog
- `/` on tunnel hostname — static storefront landing page (busybox httpd)

## Key Packages

| Package | Key Files | Role |
|---------|-----------|------|
| `cmd/obol` | `main.go`, `sell.go`, `network.go`, `openclaw.go`, `model.go` | CLI commands |
| `internal/config` | `config.go` | XDG Config struct |
| `internal/stack` | `stack.go` | Cluster lifecycle |
| `internal/network` | `network.go`, `erpc.go`, `rpc.go`, `parser.go` | Networks, eRPC, RPC gateway |
| `internal/x402` | `config.go`, `setup.go`, `verifier.go`, `matcher.go`, `watcher.go` | ForwardAuth verifier |
| `internal/x402/buyer` | `signer.go`, `proxy.go`, `config.go` | Buy-side sidecar |
| `internal/erc8004` | `client.go`, `types.go`, `abi.go` | ERC-8004 Identity Registry |
| `internal/agent` | `agent.go` | obol-agent singleton, RBAC patching |
| `internal/model` | `model.go` | LiteLLM gateway configuration |
| `internal/openclaw` | `openclaw.go`, `wallet.go`, `resolve.go` | OpenClaw setup, wallet, instance resolution |
| `internal/inference` | `gateway.go`, `container.go`, `store.go` | Standalone x402 gateway |
| `internal/enclave` | `enclave.go`, `enclave_darwin.go`, `enclave_stub.go` | Secure Enclave keys |
| `internal/embed` | `embed.go` | Embedded assets (skills, infrastructure, networks) |

**Embedded assets**: `internal/embed/infrastructure/` (K8s templates), `internal/embed/networks/` (ethereum, helios, aztec), `internal/embed/skills/` (23 skills).

**Tests**: `cmd/obol/sell_test.go` (CLI flags), `internal/x402/*_test.go` (verifier, config, matcher, E2E), `internal/erc8004/*_test.go` (ABI, client), `internal/embed/embed_crd_test.go` (CRD+RBAC validation), `internal/openclaw/integration_test.go` (full-cluster inference), `internal/openclaw/overlay_test.go`, `internal/inference/gateway_test.go`.

**Docs**: `docs/guides/monetize-inference.md` (E2E monetize walkthrough), `README.md`.
## Dependencies

**Deps**: Docker 20.10.0+, Go 1.25+. Installed by obolup.sh: kubectl 1.35.0, helm 3.19.4, k3d 5.8.3, helmfile 1.2.3, k9s 0.50.18, helm-diff 3.14.1. Key Go: `urfave/cli/v3`, `dustinkirkland/golang-petname`, `mark3labs/x402-go`.
Docker 20.10.0+, Go 1.25+. Toolchain installed by `obolup.sh` (kubectl, helm, k3d, helmfile, k9s). Key Go deps: `urfave/cli/v3`, `dustinkirkland/golang-petname`, `mark3labs/x402-go`. E2E monetize walkthrough: `@docs/guides/monetize-inference.md`.

## Related Codebases

Expand Down
12 changes: 12 additions & 0 deletions Dockerfile.reth-erc8004-indexer
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM rust:1.93-bookworm AS builder

WORKDIR /build

COPY reth-erc8004-indexer/Cargo.toml reth-erc8004-indexer/Cargo.toml
COPY reth-erc8004-indexer/src reth-erc8004-indexer/src

RUN cargo build --release --manifest-path reth-erc8004-indexer/Cargo.toml

FROM ghcr.io/paradigmxyz/reth:v1.11.1

COPY --from=builder /build/target/release/reth /usr/local/bin/reth
33 changes: 33 additions & 0 deletions Dockerfile.worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
python3 \
python3-pip \
python3-venv \
&& rm -rf /var/lib/apt/lists/*

# Install uv for running the autoresearch repo environment.
RUN curl -LsSf https://astral.sh/uv/install.sh | sh \
&& mv /root/.local/bin/uv /usr/local/bin/uv

WORKDIR /app

COPY internal/embed/skills/autoresearch-worker/scripts/worker_api.py /app/worker_api.py

ENV DATA_DIR=/data \
AUTORESEARCH_REPO=/data/autoresearch \
EXPERIMENT_TIMEOUT_SECONDS=300 \
TRAIN_COMMAND="uv run train.py"

RUN useradd -m -s /bin/bash worker && mkdir -p /data && chown worker:worker /data

USER worker
VOLUME ["/data"]
EXPOSE 8080

ENTRYPOINT ["python3", "/app/worker_api.py", "serve", "--repo", "/data/autoresearch", "--data-dir", "/data", "--host", "0.0.0.0", "--port", "8080"]
Loading
Loading