Skip to content
Open
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
24 changes: 23 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
# explorer block explorer UI
# rewards-eligibility REO eligibility oracle node
# indexing-payments dipper + iisa (requires GHCR auth — see README)
# studio subgraph studio (ui, api, query-proxy, deployment-router, redis - requires local clone)
# Default: profiles that work out of the box.
COMPOSE_PROFILES=block-oracle,explorer
# All profiles (indexing-payments requires GHCR auth — see README):
#COMPOSE_PROFILES=rewards-eligibility,block-oracle,explorer,indexing-payments
#COMPOSE_PROFILES=rewards-eligibility,block-oracle,explorer,indexing-payments,studio

# --- Dev overrides ---
# Uncomment and extend to build services from local source.
Expand Down Expand Up @@ -72,6 +73,18 @@ REDPANDA_SCHEMA_REGISTRY_PORT=18081
GRAPH_TALLY_AGGREGATOR_PORT=7610
BLOCK_EXPLORER_PORT=3000

# Studio ports (only effective when COMPOSE_PROFILES includes `studio`)
# STUDIO_UI_PORT must stay at 5000 — WalletConnect metadata URL is hardcoded
# to port 5000 in the studio UI's wagmi config.
STUDIO_UI_PORT=5000
STUDIO_API_PORT=4000
STUDIO_API_METRICS_PORT=9090
STUDIO_REDIS_PORT=6379
STUDIO_DEPLOYMENT_ROUTER_PORT=4001
STUDIO_DEPLOYMENT_ROUTER_METRICS_PORT=9091
STUDIO_QUERY_PROXY_PORT=4002
STUDIO_QUERY_PROXY_METRICS_PORT=9092

# backward compat: old names without _PORT suffix (shell-only, uses ${} expansion)
# docker-compose sees these as literal strings — use _PORT names in docker-compose.yaml
# TODO: remove once all consumers (other repos) are migrated to _PORT names
Expand Down Expand Up @@ -131,3 +144,12 @@ REO_ORACLE_UPDATE_TIMEOUT=86400

# Gateway
GATEWAY_API_KEY="deadbeefdeadbeefdeadbeefdeadbeef"

# Studio (test-mode credentials — placeholders are fine for the local stack;
# set real test values in .env.local if exercising paid flows).
STUDIO_STRIPE_SECRET_KEY=
STUDIO_STRIPE_PUBLISHABLE_KEY=
STUDIO_ORB_API_KEY=
STUDIO_ORB_GROWTH_PLAN_ID=
STUDIO_ORB_ANALYTICS_PLAN_ID=
STUDIO_SAFE_API_KEY=
26 changes: 26 additions & 0 deletions CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,29 @@ curl "http://localhost:7700/api/subgraphs/id/BFr2mx7FgkJ36Y6pE5BiXs1KmNUmVDCnL82
docker exec -it redpanda rpk topic consume gateway_client_query_results --brokers="localhost:9092"
```

### studio

Requires `STUDIO_SOURCE_ROOT` and `COMPOSE_FILE=docker-compose.yaml:compose/dev/studio.yaml` —
see `compose/dev/README.md`.

- UI: http://localhost:5000/studio
- API: http://localhost:4000 — `/graphql`, `/.well-known/jwks.json`
- Redis: `redis-cli -p 6379 ping`

Seed a verified studio user so a MetaMask wallet can sign in and publish
without the email-confirmation prompt:

```bash
./scripts/seed-studio-user.sh 0xYOUR_METAMASK_ADDRESS
# e.g. ACCOUNT0: ./scripts/seed-studio-user.sh 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
```

To publish a subgraph from Studio, the connected MetaMask wallet must have ETH
on the local chain to cover gas. Fund it then mine a block so MetaMask picks
up the new balance:

```bash
./scripts/fund-wallet.sh <metamask_address> [amount_in_eth]
./scripts/mine-block.sh
```

56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ Available profiles:
| `explorer` | block-explorer UI | none |
| `rewards-eligibility` | eligibility-oracle-node | none (clones from GitHub) |
| `indexing-payments` | dipper, iisa, iisa-scoring | GHCR auth (below) |
| `studio` | studio-api, studio-ui, studio-query-proxy, studio-deployment-router, studio-redis | local subgraph-studio checkout (see below) |

To enable all profiles, uncomment the full line in `.env`:

```bash
COMPOSE_PROFILES=rewards-eligibility,block-oracle,explorer,indexing-payments
COMPOSE_PROFILES=rewards-eligibility,block-oracle,explorer,indexing-payments,studio
```

### GHCR authentication (indexing-payments)
Expand Down Expand Up @@ -98,6 +99,59 @@ Each override requires a binary path env var. Source repos own their own build;
local-network just wraps the published image with `run.sh` and utilities.
See [compose/dev/README.md](compose/dev/README.md) for details.

## Studio quickstart

The `studio` profile runs Subgraph Studio (UI, API, query-proxy,
deployment-router, redis) against the local chain. It currently requires a
local `subgraph-studio` checkout mounted via a dev override — no
local-targeted image is published yet. See
[compose/dev/README.md](compose/dev/README.md#studio-dev-override) for the full
rationale and the planned migration to a prebuilt image.

1. Clone `subgraph-studio`, then install and build from inside the repo:
```bash
bun install
bun run build
```
2. Point local-network at the checkout and enable the profile + override in
`.env.local`:
```bash
STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio
COMPOSE_FILE=docker-compose.yaml:compose/dev/studio.yaml
COMPOSE_PROFILES=studio
```
3. Start the stack:
```bash
docker compose up -d
```
4. Connect a wallet to the local chain (see [Wallet setup](#wallet-setup) below).
5. Seed a verified user and fund the wallet so it can sign in and publish
without the email-confirmation prompt:
```bash
./scripts/seed-studio-user.sh <your_wallet_address>
./scripts/fund-wallet.sh <your_wallet_address> 1 && ./scripts/mine-block.sh
```
6. Open the UI at http://localhost:5000/studio/

`STUDIO_UI_PORT` must stay at 5000 — WalletConnect's metadata URL is hardcoded
to that port in the studio UI.

### Wallet setup

To sign in and publish from Studio, connect a wallet (e.g. MetaMask) to the
local chain by adding a custom network:

- **RPC URL:** `http://localhost:8545` (`CHAIN_RPC_PORT`)
- **Chain ID:** `1337` (`CHAIN_ID`)
- **Currency symbol:** ETH

Use your own test account — seed and fund that address with the scripts above.
Never connect a wallet holding real funds to a local dev chain.

**macOS note:** the UI is pinned to port 5000, which macOS AirPlay Receiver
binds by default. If http://localhost:5000/studio/ is unreachable, disable it
under System Settings → General → AirDrop & Handoff → AirPlay Receiver.

## Devcontainer usage

When running inside a devcontainer, service names (gateway, redpanda, etc.) won't resolve by default because the devcontainer is on a different Docker network. Connect it to the compose network once per session:
Expand Down
51 changes: 51 additions & 0 deletions compose/dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,56 @@ Then `docker compose up -d` applies the overrides automatically.
| `eligibility-oracle.yaml` | eligibility-oracle-node | `REO_BINARY` |
| `dipper.yaml` | dipper | `DIPPER_BINARY` |
| `iisa.yaml` | iisa | `IISA_VERSION=local` |
| `studio.yaml` | studio-api, studio-ui, studio-query-proxy, studio-deployment-router | `STUDIO_SOURCE_ROOT` |

See each file's header comments for details.

## Studio (dev override)

Mounts a local `subgraph-studio` checkout at `/app` so the four studio app
services (`studio-api`, `studio-ui`, `studio-query-proxy`,
`studio-deployment-router`) run against your working tree. This override is
**required** today — no local-network-targeted subgraph-studio image is
published yet (see "Future" below). Without it, the four services start, find
no source at `/app`, and exit with a helpful message.

`studio-redis` runs unconditionally (no override needed) once the `studio`
profile is active.

**URL:** http://localhost:5000/studio/

### Prerequisites

1. Clone `subgraph-studio` — the local-Hardhat patches (chain entry, deployment
router LOCAL mode, optional `LOCAL_*` env vars) are now on `main`.
Then set `STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio` in `.env` or `.env.local`.
2. From inside the studio repo, run install and build:
```bash
bun install
bun run build
```
3. Enable the override and the studio profile in `.env` or `.env.local` :
```bash
COMPOSE_FILE=docker-compose.yaml:compose/dev/studio.yaml
COMPOSE_PROFILES=block-oracle,explorer,studio
STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio
```

### Port 5000

`STUDIO_UI_PORT` must stay at 5000 — WalletConnect's metadata URL is
hardcoded to that port in the studio UI.

### Future: switch to a prebuilt GHCR image

The current base compose builds `containers/ui/studio/dev/Dockerfile`
(node + bun, no source baked in). It's a placeholder for an image that doesn't
exist yet. Subgraph-studio already publishes to GHCR staging and production build
images, but both bake their URLs into the UI bundle at build time so a local network
version is still required.

Migration to image-pull mode requires a CI job on `edgeandnode/subgraph-studio`
that builds an image with `localhost`-targeted URLs and pushes it to GHCR.
Once that exists, swap `build: { context: containers/ui/studio/dev }` in
`docker-compose.yaml` for `image: ghcr.io/...:<tag>` and drop the dev
override + the dev/Dockerfile. The per-service `.sh` env wrappers stay.
29 changes: 29 additions & 0 deletions compose/dev/studio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Studio dev override — mounts a local subgraph-studio checkout into the
# studio app services. Required today because no local-network-targeted
# subgraph-studio image is published yet (see "Future" in compose/dev/README.md).
#
# Activate via .env.local:
# STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio
# COMPOSE_FILE=docker-compose.yaml:compose/dev/studio.yaml
# COMPOSE_PROFILES=block-oracle,explorer,studio
#
# Prerequisites in the studio checkout:
# bun install
# bun run build # builds packages/shared (and others)

services:
studio-api:
volumes:
- ${STUDIO_SOURCE_ROOT}:/app

studio-ui:
volumes:
- ${STUDIO_SOURCE_ROOT}:/app

studio-deployment-router:
volumes:
- ${STUDIO_SOURCE_ROOT}:/app

studio-query-proxy:
volumes:
- ${STUDIO_SOURCE_ROOT}:/app
1 change: 1 addition & 0 deletions containers/core/postgres/setup.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
CREATE DATABASE graph_node_1;
CREATE DATABASE indexer_components_1;
CREATE DATABASE dipper_1;
CREATE DATABASE studio;
35 changes: 35 additions & 0 deletions containers/ui/studio/api.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash
# Studio API env wrapper. Sources local-network .env, exports only the values
# that diverge from studio's built-in defaults (or that we want explicit for
# clarity), then execs the passed command. With no args, runs the prod startup.
set -eu
. /opt/config/.env

# Studio defaults don't match our stack:
export DB_NAME=studio
export DB_USER=postgres
export DB_PASS=postgres
export DATABASE_URL="postgresql://postgres:postgres@postgres:5432/studio"
export REDIS_HOST=studio-redis
export ALLOWED_IMAGE_HOSTS="http://ipfs:5001"
export INFURA_KEY="unused-local"

# Explicit even though current studio defaults match — pin for clarity:
export REDIS_PORT=6379
export QUERY_NODE_BASE_URL="http://graph-node:8000"
export NETWORK_ID=1337

# Stripe (warning if empty, non-fatal); flow through .env.local overrides
export STRIPE_SECRET_KEY="${STUDIO_STRIPE_SECRET_KEY}"
export STRIPE_PUBLISHABLE_KEY="${STUDIO_STRIPE_PUBLISHABLE_KEY}"

# OrbClient throws on empty — local stubs let the server start
export ORB_API_KEY="${STUDIO_ORB_API_KEY:-local-stub}"
export ORB_GROWTH_PLAN_ID="${STUDIO_ORB_GROWTH_PLAN_ID:-local-stub}"
export ORB_ANALYTICS_PLAN_ID="${STUDIO_ORB_ANALYTICS_PLAN_ID:-local-stub}"

if [ $# -eq 0 ]; then
set -- bash -c "cd /app/packages/shared && bun run db:setup && cd /app/packages/api && exec node ."
fi

exec "$@"
22 changes: 22 additions & 0 deletions containers/ui/studio/deployment-router.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# Studio deployment-router env wrapper. Sources local-network .env, exports
# only the values that diverge from studio defaults, then execs the passed
# command. With no args, runs the prod startup (node dist/server.mjs).
set -eu
. /opt/config/.env

# Studio defaults don't match our stack:
export DB_NAME=studio
export DB_USER=postgres
export DB_PASS=postgres
export DATABASE_URL="postgresql://postgres:postgres@postgres:5432/studio"
export REDIS_HOST=studio-redis

# Point at the docker-network address for studio-query-proxy
export QUERY_PROXY_BASE_URL="http://studio-query-proxy:${STUDIO_QUERY_PROXY_PORT:-4002}/query"

if [ $# -eq 0 ]; then
set -- bash -c "cd /app/packages/deployment-router && exec node dist/server.mjs"
fi

exec "$@"
7 changes: 7 additions & 0 deletions containers/ui/studio/dev/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM node:22.14-slim
RUN apt-get -qy update && apt-get -qy install --no-install-recommends \
ca-certificates curl git openssl unzip \
&& rm -rf /var/lib/apt/lists/*
RUN BUN_INSTALL=/root/.bun curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:${PATH}"
WORKDIR /app
7 changes: 7 additions & 0 deletions containers/ui/studio/dev/clear-metrics.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Workaround for prom-client double-registration with bun's module resolution.
// Clears the default metrics registry before the app starts.
try {
require('prom-client').register.clear()
} catch (e) {
// prom-client not available yet, that's fine
}
19 changes: 19 additions & 0 deletions containers/ui/studio/query-proxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Studio query-proxy env wrapper. Sources local-network .env, exports only the
# values that diverge from studio defaults, then execs the passed command.
# With no args, runs the prod startup (node dist/server.mjs).
set -eu
. /opt/config/.env

# Studio defaults don't match our stack:
export DB_NAME=studio
export DB_USER=postgres
export DB_PASS=postgres
export DATABASE_URL="postgresql://postgres:postgres@postgres:5432/studio"
export REDIS_URL="redis://studio-redis:6379"

if [ $# -eq 0 ]; then
set -- bash -c "cd /app/packages/query-proxy && exec node dist/server.mjs"
fi

exec "$@"
44 changes: 44 additions & 0 deletions containers/ui/studio/ui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash
# Studio UI env wrapper. Sources local-network .env, exports only the values
# that diverge from studio UI defaults (or that the UI strictly requires),
# then execs the passed command. With no args, runs the prod startup.
set -eu
. /opt/config/.env

# Required by studio UI (packages/ui/src/env/inlinedEnv.mjs throws on falsy):
export STUDIO_CLIENT_SIDE_GATEWAY_API_KEY="${GATEWAY_API_KEY}"
export STUDIO_GRAPHQL_HTTP_URI="http://localhost:${STUDIO_API_PORT}/graphql"
export STUDIO_GRAPHQL_WS_URI="ws://localhost:${STUDIO_API_PORT}/graphql"
export BASE_URI="http://localhost:${STUDIO_UI_PORT}"
export NETWORK_ID=1337
export INFURA_KEY="unused-local"
export ENVIRONMENT=local
export SAFE_API_KEY="${STUDIO_SAFE_API_KEY:-local-stub}"

# UI defaults point elsewhere — override for local stack:
export STUDIO_GRAPHQL_URI_SSR="http://studio-api:4000/graphql"
export STUDIO_JWKS_URI="http://studio-api:4000/.well-known/jwks.json"
# NOTE: IPFS_API_URL intentionally not exported — packages/ui/src/env/inlinedEnv.mjs
# hardcodes 'https://ipfs.thegraph.com' (no process.env read), so any value here
# is a no-op. Local IPFS image rendering needs an upstream patch.

# Local Hardhat chain addresses (deterministic from test mnemonic).
# TODO: read from /opt/config/horizon.json via lib.sh for robustness.
export LOCAL_GNS_ADDRESS="0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E"
export LOCAL_GRAPH_TOKEN_ADDRESS="0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690"
export LOCAL_L2_GRAPH_TOKEN_GATEWAY_ADDRESS="0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB"
export GRAPH_NETWORK_LOCAL_GRAPHQL_URI="http://localhost:${GRAPH_NODE_GRAPHQL_PORT}/subgraphs/name/graph-network"

# Billing — required by UI inlinedEnv but local doesn't exercise paid flows.
# GraphQL points at a non-routable host; addrs are zero; chain ID must be one
# that @edgeandnode/common's getCaip2IdFromChainId knows about like Sepolia (11155111)
export BILLING_GRAPHQL_HTTP_URI="http://billing-not-configured.local"
export BILLING_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000"
export BILLING_CONNECTOR_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000"
export BILLING_CONNECTOR_CONTRACT_CHAIN_ID="11155111"

if [ $# -eq 0 ]; then
set -- bash -c "cd /app/packages/ui && exec npx next start -p ${STUDIO_UI_PORT}"
fi

exec "$@"
Loading