From 035086df7b63a2a40b221f967b5a537a635bf733 Mon Sep 17 00:00:00 2001 From: Nas Date: Mon, 25 May 2026 12:03:15 +1000 Subject: [PATCH 1/5] feat: add studio profile with studio-redis and studio-api dev override --- .env | 19 ++++++++- compose/dev/studio.yaml | 17 ++++++++ containers/core/postgres/setup.sql | 1 + containers/ui/studio/api.sh | 35 ++++++++++++++++ containers/ui/studio/dev/Dockerfile | 7 ++++ containers/ui/studio/dev/clear-metrics.cjs | 7 ++++ docker-compose.yaml | 48 ++++++++++++++++++++++ 7 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 compose/dev/studio.yaml create mode 100755 containers/ui/studio/api.sh create mode 100644 containers/ui/studio/dev/Dockerfile create mode 100644 containers/ui/studio/dev/clear-metrics.cjs diff --git a/.env b/.env index f0413c4f..f25a0fe6 100644 --- a/.env +++ b/.env @@ -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. @@ -72,6 +73,14 @@ 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 + # 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 @@ -131,3 +140,11 @@ 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= diff --git a/compose/dev/studio.yaml b/compose/dev/studio.yaml new file mode 100644 index 00000000..34d5da2a --- /dev/null +++ b/compose/dev/studio.yaml @@ -0,0 +1,17 @@ +# 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 diff --git a/containers/core/postgres/setup.sql b/containers/core/postgres/setup.sql index 75d27740..65eba454 100644 --- a/containers/core/postgres/setup.sql +++ b/containers/core/postgres/setup.sql @@ -1,3 +1,4 @@ CREATE DATABASE graph_node_1; CREATE DATABASE indexer_components_1; CREATE DATABASE dipper_1; +CREATE DATABASE studio; diff --git a/containers/ui/studio/api.sh b/containers/ui/studio/api.sh new file mode 100755 index 00000000..9cb06a50 --- /dev/null +++ b/containers/ui/studio/api.sh @@ -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 "$@" diff --git a/containers/ui/studio/dev/Dockerfile b/containers/ui/studio/dev/Dockerfile new file mode 100644 index 00000000..bf7cd95c --- /dev/null +++ b/containers/ui/studio/dev/Dockerfile @@ -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 diff --git a/containers/ui/studio/dev/clear-metrics.cjs b/containers/ui/studio/dev/clear-metrics.cjs new file mode 100644 index 00000000..b311db1b --- /dev/null +++ b/containers/ui/studio/dev/clear-metrics.cjs @@ -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 +} diff --git a/docker-compose.yaml b/docker-compose.yaml index 218ffd8f..ccb1b8c7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -397,6 +397,54 @@ services: ] restart: on-failure:3 + # --- Studio (activated via COMPOSE_PROFILES) --- + # Studio app services require a local subgraph-studio checkout mounted at /app + # via compose/dev/studio.yaml. Without that override, containers start, find + # no source, and exit with a helpful message. Stage 6 (see compose/dev/README.md) + # plans to replace the dev/Dockerfile build with a published GHCR image. + + studio-redis: + container_name: studio-redis + profiles: [studio] + image: redis:7-alpine + ports: ["${STUDIO_REDIS_PORT}:6379"] + healthcheck: { interval: 1s, retries: 20, test: redis-cli ping } + restart: on-failure:3 + + studio-api: + container_name: studio-api + profiles: [studio] + build: { context: containers/ui/studio/dev } + entrypoint: bash /opt/run.sh + command: + - bash + - -c + - | + if [ ! -d /app/packages/api ]; then + echo "studio-api needs subgraph-studio source mounted at /app." + echo "Activate compose/dev/studio.yaml and set STUDIO_SOURCE_ROOT — see compose/dev/README.md." + exit 1 + fi + cd /app/packages/shared && bun run db:setup + cd /app/packages/api && exec npx tsx --require /opt/clear-metrics.cjs src/server.ts + depends_on: + postgres: { condition: service_healthy } + studio-redis: { condition: service_healthy } + graph-contracts: { condition: service_completed_successfully } + ipfs: { condition: service_healthy } + ports: + - "${STUDIO_API_PORT}:4000" + - "${STUDIO_API_METRICS_PORT}:9090" + volumes: + - ./containers/ui/studio/api.sh:/opt/run.sh:ro + - ./containers/ui/studio/dev/clear-metrics.cjs:/opt/clear-metrics.cjs:ro + - ./.env:/opt/config/.env:ro + healthcheck: + test: curl -sf http://127.0.0.1:4000/ || exit 1 + interval: 10s + retries: 60 + restart: on-failure:3 + # --- Readiness check --- ready: From b27de934a2e0678f33b92ec0aca16bd72ca292c4 Mon Sep 17 00:00:00 2001 From: Nas Date: Mon, 25 May 2026 13:35:55 +1000 Subject: [PATCH 2/5] feat: add studio-ui with dev override and seed-studio-user helper --- .env | 1 + CHEATSHEET.md | 17 ++++++++++++++ compose/dev/studio.yaml | 4 ++++ containers/ui/studio/ui.sh | 44 +++++++++++++++++++++++++++++++++++++ docker-compose.yaml | 28 +++++++++++++++++++++++ scripts/seed-studio-user.sh | 25 +++++++++++++++++++++ 6 files changed, 119 insertions(+) create mode 100755 containers/ui/studio/ui.sh create mode 100755 scripts/seed-studio-user.sh diff --git a/.env b/.env index f25a0fe6..b97ac6d7 100644 --- a/.env +++ b/.env @@ -148,3 +148,4 @@ STUDIO_STRIPE_PUBLISHABLE_KEY= STUDIO_ORB_API_KEY= STUDIO_ORB_GROWTH_PLAN_ID= STUDIO_ORB_ANALYTICS_PLAN_ID= +STUDIO_SAFE_API_KEY= diff --git a/CHEATSHEET.md b/CHEATSHEET.md index e1ae4e14..5859cd5d 100644 --- a/CHEATSHEET.md +++ b/CHEATSHEET.md @@ -130,3 +130,20 @@ 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 +``` + diff --git a/compose/dev/studio.yaml b/compose/dev/studio.yaml index 34d5da2a..b95bf104 100644 --- a/compose/dev/studio.yaml +++ b/compose/dev/studio.yaml @@ -15,3 +15,7 @@ services: studio-api: volumes: - ${STUDIO_SOURCE_ROOT}:/app + + studio-ui: + volumes: + - ${STUDIO_SOURCE_ROOT}:/app diff --git a/containers/ui/studio/ui.sh b/containers/ui/studio/ui.sh new file mode 100755 index 00000000..f434713d --- /dev/null +++ b/containers/ui/studio/ui.sh @@ -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 "$@" diff --git a/docker-compose.yaml b/docker-compose.yaml index ccb1b8c7..c6716be3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -445,6 +445,34 @@ services: retries: 60 restart: on-failure:3 + studio-ui: + container_name: studio-ui + profiles: [studio] + build: { context: containers/ui/studio/dev } + entrypoint: bash /opt/run.sh + command: + - bash + - -c + - | + if [ ! -d /app/packages/ui ]; then + echo "studio-ui needs subgraph-studio source mounted at /app." + echo "Activate compose/dev/studio.yaml and set STUDIO_SOURCE_ROOT — see compose/dev/README.md." + exit 1 + fi + cd /app/packages/ui && exec npx next dev -p 5000 + depends_on: + studio-api: { condition: service_healthy } + ports: + - "${STUDIO_UI_PORT}:5000" + volumes: + - ./containers/ui/studio/ui.sh:/opt/run.sh:ro + - ./.env:/opt/config/.env:ro + healthcheck: + test: curl -sf http://127.0.0.1:5000/studio/ || exit 1 + interval: 10s + retries: 60 + restart: on-failure:3 + # --- Readiness check --- ready: diff --git a/scripts/seed-studio-user.sh b/scripts/seed-studio-user.sh new file mode 100755 index 00000000..370e8f44 --- /dev/null +++ b/scripts/seed-studio-user.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Seed a fully-verified studio user for an ETH address. Lets the wallet sign in +# via MetaMask and immediately publish a subgraph without email confirmation. +# Wraps subgraph-studio's packages/shared/src/database/seeds/seed-local-user.ts. +# +# Usage: seed-studio-user.sh + +set -euo pipefail + +ADDRESS="${1:-}" +if [[ -z "$ADDRESS" ]]; then + echo "Usage: $0 " + exit 1 +fi + +# docker compose exec doesn't inherit api.sh's runtime exports — pass the DB +# connection env vars explicitly so the knex CLI hits the right database. +docker compose exec \ + -e ETH_ADDRESS="$ADDRESS" \ + -e DB_NAME=studio \ + -e DB_USER=postgres \ + -e DB_PASS=postgres \ + studio-api bash -c \ + "cd /app/packages/shared && npm run knex -- seed:run --specific seed-local-user.ts" From 1b0fd786839725bcbb540c7bbb1662c5f6547cf8 Mon Sep 17 00:00:00 2001 From: Nas Date: Mon, 25 May 2026 13:58:02 +1000 Subject: [PATCH 3/5] feat: add studio-deployment-router and fund-wallet script --- .env | 2 ++ CHEATSHEET.md | 9 +++++++ compose/dev/studio.yaml | 4 +++ containers/ui/studio/deployment-router.sh | 22 ++++++++++++++++ docker-compose.yaml | 32 +++++++++++++++++++++++ scripts/fund-wallet.sh | 31 ++++++++++++++++++++++ 6 files changed, 100 insertions(+) create mode 100755 containers/ui/studio/deployment-router.sh create mode 100755 scripts/fund-wallet.sh diff --git a/.env b/.env index b97ac6d7..391037e4 100644 --- a/.env +++ b/.env @@ -80,6 +80,8 @@ 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 # 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 diff --git a/CHEATSHEET.md b/CHEATSHEET.md index 5859cd5d..0db7d376 100644 --- a/CHEATSHEET.md +++ b/CHEATSHEET.md @@ -147,3 +147,12 @@ without the email-confirmation prompt: # 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 [amount_in_eth] +./scripts/mine-block.sh +``` + diff --git a/compose/dev/studio.yaml b/compose/dev/studio.yaml index b95bf104..d10e08f0 100644 --- a/compose/dev/studio.yaml +++ b/compose/dev/studio.yaml @@ -19,3 +19,7 @@ services: studio-ui: volumes: - ${STUDIO_SOURCE_ROOT}:/app + + studio-deployment-router: + volumes: + - ${STUDIO_SOURCE_ROOT}:/app diff --git a/containers/ui/studio/deployment-router.sh b/containers/ui/studio/deployment-router.sh new file mode 100755 index 00000000..fdc7d055 --- /dev/null +++ b/containers/ui/studio/deployment-router.sh @@ -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 "$@" diff --git a/docker-compose.yaml b/docker-compose.yaml index c6716be3..3a53b13c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -473,6 +473,38 @@ services: retries: 60 restart: on-failure:3 + studio-deployment-router: + container_name: studio-deployment-router + profiles: [studio] + build: { context: containers/ui/studio/dev } + entrypoint: bash /opt/run.sh + command: + - bash + - -c + - | + if [ ! -d /app/packages/deployment-router ]; then + echo "studio-deployment-router needs subgraph-studio source mounted at /app." + echo "Activate compose/dev/studio.yaml and set STUDIO_SOURCE_ROOT — see compose/dev/README.md." + exit 1 + fi + cd /app/packages/deployment-router && exec npx tsx src/server.ts + depends_on: + studio-api: { condition: service_healthy } + postgres: { condition: service_healthy } + graph-node: { condition: service_healthy } + ipfs: { condition: service_healthy } + ports: + - "${STUDIO_DEPLOYMENT_ROUTER_PORT}:4001" + - "${STUDIO_DEPLOYMENT_ROUTER_METRICS_PORT}:9091" + volumes: + - ./containers/ui/studio/deployment-router.sh:/opt/run.sh:ro + - ./.env:/opt/config/.env:ro + healthcheck: + test: curl -sf http://127.0.0.1:4001/deploy || exit 1 + interval: 10s + retries: 60 + restart: on-failure:3 + # --- Readiness check --- ready: diff --git a/scripts/fund-wallet.sh b/scripts/fund-wallet.sh new file mode 100755 index 00000000..d798dd4e --- /dev/null +++ b/scripts/fund-wallet.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Fund a wallet address with ETH on the local Hardhat chain. +# Usage: fund-wallet.sh
[amount_in_eth] + +set -euo pipefail + +source "$(dirname "$0")/../.env" +CHAIN_HOST="${CHAIN_HOST:-localhost}" + +ADDRESS="${1:-}" +AMOUNT_ETH="${2:-1}" + +if [[ -z "$ADDRESS" ]]; then + echo "Usage: $0
[amount_in_eth]" + echo " address wallet address to fund" + echo " amount_in_eth amount in ETH (default: 1)" + exit 1 +fi + +# Convert ETH to wei (hex) — use bc for both to avoid 64-bit integer overflow +AMOUNT_WEI_HEX=0x$(echo "obase=16; $AMOUNT_ETH * 10^18 / 1" | bc) + +echo "Funding $ADDRESS with $AMOUNT_ETH ETH ($AMOUNT_WEI_HEX wei)..." + +curl -s -X POST "http://${CHAIN_HOST}:${CHAIN_RPC_PORT}" \ + -H 'Content-Type: application/json' \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"hardhat_setBalance\",\"params\":[\"$ADDRESS\",\"$AMOUNT_WEI_HEX\"],\"id\":1}" \ + | jq . + +echo "Done." From 1a9305030beb65e272d40e4af71577967cdc4745 Mon Sep 17 00:00:00 2001 From: Nas Date: Mon, 25 May 2026 14:13:49 +1000 Subject: [PATCH 4/5] feat: add studio-query-proxy and dev override docs. Update README --- .env | 2 ++ compose/dev/README.md | 50 +++++++++++++++++++++++++++++ compose/dev/studio.yaml | 4 +++ containers/ui/studio/query-proxy.sh | 19 +++++++++++ docker-compose.yaml | 30 +++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100755 containers/ui/studio/query-proxy.sh diff --git a/.env b/.env index 391037e4..0fa73b1c 100644 --- a/.env +++ b/.env @@ -82,6 +82,8 @@ 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 diff --git a/compose/dev/README.md b/compose/dev/README.md index b21b5ccd..a9fcda81 100644 --- a/compose/dev/README.md +++ b/compose/dev/README.md @@ -31,5 +31,55 @@ 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` and check out the `nas/ui-hardhat-publish` branch — + it carries the local-Hardhat patches (chain entry, deployment router LOCAL + mode, optional `LOCAL_*` env vars) not yet on `main`. + Then set `STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio` in `.env.local`. +2. From inside the studio repo, install once: + ```bash + bun install + ``` +3. Enable the override and the studio profile in `.env.local`: + ```bash + COMPOSE_FILE=docker-compose.yaml:compose/dev/studio.yaml + COMPOSE_PROFILES=block-oracle,explorer,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/...:` and drop the dev +override + the dev/Dockerfile. The per-service `.sh` env wrappers stay. diff --git a/compose/dev/studio.yaml b/compose/dev/studio.yaml index d10e08f0..dc58d2aa 100644 --- a/compose/dev/studio.yaml +++ b/compose/dev/studio.yaml @@ -23,3 +23,7 @@ services: studio-deployment-router: volumes: - ${STUDIO_SOURCE_ROOT}:/app + + studio-query-proxy: + volumes: + - ${STUDIO_SOURCE_ROOT}:/app diff --git a/containers/ui/studio/query-proxy.sh b/containers/ui/studio/query-proxy.sh new file mode 100755 index 00000000..360e4a25 --- /dev/null +++ b/containers/ui/studio/query-proxy.sh @@ -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 "$@" diff --git a/docker-compose.yaml b/docker-compose.yaml index 3a53b13c..79c5b98d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -505,6 +505,36 @@ services: retries: 60 restart: on-failure:3 + studio-query-proxy: + container_name: studio-query-proxy + profiles: [studio] + build: { context: containers/ui/studio/dev } + entrypoint: bash /opt/run.sh + command: + - bash + - -c + - | + if [ ! -d /app/packages/query-proxy ]; then + echo "studio-query-proxy needs subgraph-studio source mounted at /app." + echo "Activate compose/dev/studio.yaml and set STUDIO_SOURCE_ROOT — see compose/dev/README.md." + exit 1 + fi + cd /app/packages/query-proxy && exec npx tsx src/server.ts + depends_on: + studio-api: { condition: service_healthy } + studio-redis: { condition: service_healthy } + ports: + - "${STUDIO_QUERY_PROXY_PORT}:4002" + - "${STUDIO_QUERY_PROXY_METRICS_PORT}:9092" + volumes: + - ./containers/ui/studio/query-proxy.sh:/opt/run.sh:ro + - ./.env:/opt/config/.env:ro + healthcheck: + test: curl -sf http://127.0.0.1:9092/metrics || exit 1 + interval: 10s + retries: 60 + restart: on-failure:3 + # --- Readiness check --- ready: From 80bbc87584cb2f0f2dcd354e9abc80f803a54030 Mon Sep 17 00:00:00 2001 From: Nas Date: Sat, 30 May 2026 23:35:43 +1000 Subject: [PATCH 5/5] update readmes --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++- compose/dev/README.md | 13 +++++----- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e5478a8e..b49206d4 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 + ./scripts/fund-wallet.sh 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: diff --git a/compose/dev/README.md b/compose/dev/README.md index a9fcda81..c54c0ccc 100644 --- a/compose/dev/README.md +++ b/compose/dev/README.md @@ -51,18 +51,19 @@ profile is active. ### Prerequisites -1. Clone `subgraph-studio` and check out the `nas/ui-hardhat-publish` branch — - it carries the local-Hardhat patches (chain entry, deployment router LOCAL - mode, optional `LOCAL_*` env vars) not yet on `main`. - Then set `STUDIO_SOURCE_ROOT=/abs/path/to/subgraph-studio` in `.env.local`. -2. From inside the studio repo, install once: +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.local`: +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