onboarding-harness: combined gate+buyer-pays brief; probe mints .live-env
run-stage2.sh: rewrite AGENT_BRIEF to the four-step operator-order journey (define a paid product + entitlement, integrate the SDK and verify the gate is BLOCKED, connect BTCPay regtest and have a buyer pay, then the PURCHASED license unlocks the gate) and add the sandbox-app section the SDK-gating half needs. Header comment updated to match. probe.sh: do what the README/brief already claim it does. In addition to the de-risk payload dump, create both stores (wallet + no-wallet), generate the on-chain regtest wallet, mint store-scoped tokens with the five documented connect permissions, and write .live-env for run-stage2.sh / validate-gate.sh to source. Previously .live-env had to be hand-built and went stale on down -v.
This commit is contained in:
@@ -1,25 +1,55 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# De-risk probe for agent-payment-connect network detection (spec §6.1).
|
# De-risk probe + .live-env minter for the Stage 2 / combined onboarding harness.
|
||||||
# Stands up a store + on-chain regtest wallet on the local BTCPay regtest stack,
|
# Run once after `docker compose -p keysat-btcpay up -d`.
|
||||||
# then dumps the exact Greenfield responses the slice-3 gate would consult:
|
#
|
||||||
# - GET /api/v1/stores/{id}/payment-methods (paymentMethodId form? derivationScheme exposed?)
|
# Two jobs:
|
||||||
# - GET /api/v1/stores/{id}/payment-methods/{pmid}/wallet/address (bcrt1… prefix?)
|
# A. Mint .live-env — create the two stores the harness needs (one with an
|
||||||
|
# on-chain regtest wallet, one without) plus store-scoped BTCPay API tokens
|
||||||
|
# carrying the five permissions the Connect-BTCPay flow documents
|
||||||
|
# (install.html#connect-btcpay), and write them to .live-env for
|
||||||
|
# run-stage2.sh / validate-gate.sh to source.
|
||||||
|
# B. De-risk (spec §6.1) — dump the exact Greenfield responses the slice-3
|
||||||
|
# network gate consults (payment-methods, wallet/address) into probe-out/
|
||||||
|
# and classify the receive-address HRP.
|
||||||
|
#
|
||||||
|
# Idempotency: assumes a FRESH instance (compose `up -d` after `down -v`).
|
||||||
|
# Re-running against a live instance creates duplicate stores — tear down first.
|
||||||
# Read-only against Keysat; only mutates the throwaway BTCPay instance.
|
# Read-only against Keysat; only mutates the throwaway BTCPay instance.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
BASE="${BTCPAY_BASE:-http://127.0.0.1:49392}"
|
BASE="${BTCPAY_BASE:-http://127.0.0.1:49392}"
|
||||||
ADMIN_EMAIL="admin@keysat.local"
|
ADMIN_EMAIL="admin@keysat.local"
|
||||||
ADMIN_PW="keysatregtest1!"
|
ADMIN_PW="keysatregtest1!"
|
||||||
OUT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/probe-out"
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
OUT_DIR="$HERE/probe-out"
|
||||||
|
LIVE_ENV="$HERE/.live-env"
|
||||||
mkdir -p "$OUT_DIR"
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
# Permissions the documented Connect-BTCPay flow grants (install.html#connect-btcpay).
|
||||||
|
STORE_PERMS='canviewstoresettings canmodifystoresettings canviewinvoices cancreateinvoice canmodifyinvoices'
|
||||||
|
BTND=keysat-btcpay-bitcoind-1
|
||||||
|
|
||||||
hr(){ printf '\n\033[1;36m=== %s ===\033[0m\n' "$*"; }
|
hr(){ printf '\n\033[1;36m=== %s ===\033[0m\n' "$*"; }
|
||||||
jqp(){ jq . 2>/dev/null || cat; }
|
jqp(){ jq . 2>/dev/null || cat; }
|
||||||
|
AUTH=(-u "$ADMIN_EMAIL:$ADMIN_PW")
|
||||||
|
cli(){ docker exec "$BTND" bitcoin-cli -regtest -rpcuser=keysat -rpcpassword=keysat -rpcport=43782 "$@"; }
|
||||||
|
|
||||||
|
create_store(){ # NAME -> store id
|
||||||
|
curl -sS "${AUTH[@]}" -X POST "$BASE/api/v1/stores" \
|
||||||
|
-H 'Content-Type: application/json' -d "{\"name\":\"$1\"}" | jq -r '.id'
|
||||||
|
}
|
||||||
|
store_token(){ # STORE_ID -> store-scoped API key with the 5 documented perms
|
||||||
|
local sid="$1" perms="" p
|
||||||
|
for p in $STORE_PERMS; do perms="$perms\"btcpay.store.$p:$sid\","; done
|
||||||
|
curl -sS "${AUTH[@]}" -X POST "$BASE/api/v1/api-keys" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"label\":\"keysat-$sid\",\"permissions\":[${perms%,}]}" | jq -r '.apiKey'
|
||||||
|
}
|
||||||
|
|
||||||
# --- 0. wait for BTCPay --------------------------------------------------------
|
# --- 0. wait for BTCPay --------------------------------------------------------
|
||||||
hr "0. waiting for BTCPay health at $BASE"
|
hr "0. waiting for BTCPay health at $BASE"
|
||||||
for i in $(seq 1 120); do
|
for i in $(seq 1 120); do
|
||||||
if curl -fsS "$BASE/api/v1/health" >/dev/null 2>&1; then break; fi
|
curl -fsS "$BASE/api/v1/health" >/dev/null 2>&1 && break
|
||||||
sleep 2
|
sleep 2
|
||||||
[[ $i == 120 ]] && { echo "BTCPay never became healthy"; exit 1; }
|
[[ $i == 120 ]] && { echo "BTCPay never became healthy"; exit 1; }
|
||||||
done
|
done
|
||||||
@@ -31,52 +61,64 @@ curl -sS -X POST "$BASE/api/v1/users" \
|
|||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PW\",\"isAdministrator\":true}" | jqp
|
-d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PW\",\"isAdministrator\":true}" | jqp
|
||||||
|
|
||||||
# Basic-auth header for subsequent Greenfield calls.
|
# --- 2. admin user API key (KEYSAT_LIVE_BTCPAY_KEY; broad, for ad-hoc admin use) -
|
||||||
AUTH=(-u "$ADMIN_EMAIL:$ADMIN_PW")
|
hr "2. mint admin user API key"
|
||||||
|
ADMIN_KEY="$(curl -sS "${AUTH[@]}" -X POST "$BASE/api/v1/api-keys" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"label":"keysat-admin","permissions":["btcpay.server.canmodifyserversettings","btcpay.store.canmodifystoresettings","btcpay.store.canmodifyinvoices"]}' \
|
||||||
|
| jq -r '.apiKey')"
|
||||||
|
echo "ADMIN_KEY=${ADMIN_KEY:0:8}…"
|
||||||
|
|
||||||
# --- 2. create a store ---------------------------------------------------------
|
# --- 3. regtest store WITH an on-chain wallet ----------------------------------
|
||||||
hr "2. create store"
|
hr "3. create regtest store (with on-chain wallet)"
|
||||||
STORE_JSON="$(curl -sS "${AUTH[@]}" -X POST "$BASE/api/v1/stores" \
|
STORE_REGTEST="$(create_store 'Keysat Regtest Co')"
|
||||||
-H 'Content-Type: application/json' -d '{"name":"Keysat Regtest Co"}')"
|
echo "STORE_REGTEST=$STORE_REGTEST"
|
||||||
echo "$STORE_JSON" | jqp
|
[[ -z "$STORE_REGTEST" || "$STORE_REGTEST" == null ]] && { echo "no regtest store id"; exit 1; }
|
||||||
STORE_ID="$(echo "$STORE_JSON" | jq -r '.id')"
|
|
||||||
echo "STORE_ID=$STORE_ID"
|
|
||||||
[[ -z "$STORE_ID" || "$STORE_ID" == null ]] && { echo "no store id"; exit 1; }
|
|
||||||
|
|
||||||
# --- 3. generate an on-chain wallet; try BTC-CHAIN then BTC --------------------
|
|
||||||
gen_body='{"savePrivateKeys":false,"importKeysToRPC":false,"wordList":"English","wordCount":12,"scriptPubKeyType":"Segwit"}'
|
gen_body='{"savePrivateKeys":false,"importKeysToRPC":false,"wordList":"English","wordCount":12,"scriptPubKeyType":"Segwit"}'
|
||||||
PMID=""
|
PMID=""
|
||||||
for cand in BTC-CHAIN BTC; do
|
for cand in BTC-CHAIN BTC; do
|
||||||
hr "3. generate wallet on pmid=$cand"
|
hr "3b. generate wallet on pmid=$cand"
|
||||||
code="$(curl -sS -o "$OUT_DIR/gen-$cand.json" -w '%{http_code}' "${AUTH[@]}" \
|
code="$(curl -sS -o "$OUT_DIR/gen-$cand.json" -w '%{http_code}' "${AUTH[@]}" \
|
||||||
-X POST "$BASE/api/v1/stores/$STORE_ID/payment-methods/$cand/wallet/generate" \
|
-X POST "$BASE/api/v1/stores/$STORE_REGTEST/payment-methods/$cand/wallet/generate" \
|
||||||
-H 'Content-Type: application/json' -d "$gen_body")"
|
-H 'Content-Type: application/json' -d "$gen_body")"
|
||||||
echo "HTTP $code"; cat "$OUT_DIR/gen-$cand.json" | jqp
|
echo "HTTP $code"; cat "$OUT_DIR/gen-$cand.json" | jqp
|
||||||
if [[ "$code" == 2* ]]; then PMID="$cand"; break; fi
|
[[ "$code" == 2* ]] && { PMID="$cand"; break; }
|
||||||
done
|
done
|
||||||
[[ -z "$PMID" ]] && echo "!! wallet generate failed for both pmid forms (see above)"
|
[[ -z "$PMID" ]] && { echo "!! wallet generate failed for both pmid forms"; exit 1; }
|
||||||
|
|
||||||
# --- 4. mine some regtest blocks so the wallet has a usable address ------------
|
# --- 4. mine regtest blocks so the wallet has a usable address -----------------
|
||||||
hr "4. mine regtest blocks"
|
hr "4. mine regtest blocks"
|
||||||
ADDR_FOR_MINE="$(docker exec keysat-btcpay-bitcoind-1 bitcoin-cli -regtest -rpcuser=keysat -rpcpassword=keysat -rpcport=43782 getnewaddress 2>/dev/null || true)"
|
ADDR_FOR_MINE="$(cli getnewaddress 2>/dev/null || true)"
|
||||||
echo "miner address: ${ADDR_FOR_MINE:-<none>}"
|
echo "miner address: ${ADDR_FOR_MINE:-<none>}"
|
||||||
if [[ -n "$ADDR_FOR_MINE" ]]; then
|
[[ -n "$ADDR_FOR_MINE" ]] && { cli generatetoaddress 101 "$ADDR_FOR_MINE" >/dev/null 2>&1 \
|
||||||
docker exec keysat-btcpay-bitcoind-1 bitcoin-cli -regtest -rpcuser=keysat -rpcpassword=keysat -rpcport=43782 generatetoaddress 101 "$ADDR_FOR_MINE" >/dev/null 2>&1 \
|
&& echo "mined 101 blocks" || echo "mine failed (non-fatal for detection probe)"; }
|
||||||
&& echo "mined 101 blocks" || echo "mine failed (non-fatal for detection probe)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 5. THE PAYLOADS the slice-3 gate consults --------------------------------
|
# --- 5. no-wallet store (fail-closed arm of the gate) --------------------------
|
||||||
hr "5a. GET payment-methods (does it expose derivationScheme? what pmid?)"
|
hr "5. create no-wallet store"
|
||||||
curl -sS "${AUTH[@]}" "$BASE/api/v1/stores/$STORE_ID/payment-methods?includeConfig=true" \
|
STORE_NOWALLET="$(create_store 'Keysat NoWallet Co')"
|
||||||
|
echo "STORE_NOWALLET=$STORE_NOWALLET"
|
||||||
|
[[ -z "$STORE_NOWALLET" || "$STORE_NOWALLET" == null ]] && { echo "no nowallet store id"; exit 1; }
|
||||||
|
|
||||||
|
# --- 6. store-scoped tokens (what the agent/harness hand Keysat at connect) -----
|
||||||
|
hr "6. mint store-scoped tokens"
|
||||||
|
GATE_TOK_REGTEST="$(store_token "$STORE_REGTEST")"
|
||||||
|
GATE_TOK_NOWALLET="$(store_token "$STORE_NOWALLET")"
|
||||||
|
echo "GATE_TOK_REGTEST=${GATE_TOK_REGTEST:0:8}… GATE_TOK_NOWALLET=${GATE_TOK_NOWALLET:0:8}…"
|
||||||
|
[[ "$GATE_TOK_REGTEST" == null || -z "$GATE_TOK_REGTEST" ]] && { echo "regtest token mint failed"; exit 1; }
|
||||||
|
|
||||||
|
# --- 7. THE PAYLOADS the slice-3 gate consults --------------------------------
|
||||||
|
hr "7a. GET payment-methods (does it expose derivationScheme? what pmid?)"
|
||||||
|
curl -sS "${AUTH[@]}" "$BASE/api/v1/stores/$STORE_REGTEST/payment-methods?includeConfig=true" \
|
||||||
| tee "$OUT_DIR/payment-methods.json" | jqp
|
| tee "$OUT_DIR/payment-methods.json" | jqp
|
||||||
|
|
||||||
hr "5b. GET wallet/address (THE network artifact — expect bcrt1…)"
|
hr "7b. GET wallet/address (THE network artifact — expect bcrt1…)"
|
||||||
ADDR_JSON="$(curl -sS "${AUTH[@]}" "$BASE/api/v1/stores/$STORE_ID/payment-methods/${PMID:-BTC-CHAIN}/wallet/address")"
|
ADDR_JSON="$(curl -sS "${AUTH[@]}" "$BASE/api/v1/stores/$STORE_REGTEST/payment-methods/${PMID:-BTC-CHAIN}/wallet/address")"
|
||||||
echo "$ADDR_JSON" | tee "$OUT_DIR/wallet-address.json" | jqp
|
echo "$ADDR_JSON" | tee "$OUT_DIR/wallet-address.json" | jqp
|
||||||
ADDR="$(echo "$ADDR_JSON" | jq -r '.address // empty')"
|
ADDR="$(echo "$ADDR_JSON" | jq -r '.address // empty')"
|
||||||
|
|
||||||
# --- 6. classify --------------------------------------------------------------
|
# --- 8. classify --------------------------------------------------------------
|
||||||
hr "6. network classification"
|
hr "8. network classification"
|
||||||
echo "pmid used : ${PMID:-BTC-CHAIN}"
|
echo "pmid used : ${PMID:-BTC-CHAIN}"
|
||||||
echo "receive address: ${ADDR:-<none>}"
|
echo "receive address: ${ADDR:-<none>}"
|
||||||
case "$ADDR" in
|
case "$ADDR" in
|
||||||
@@ -89,4 +131,16 @@ case "$ADDR" in
|
|||||||
*) echo "=> UNRECOGNIZED prefix => FAIL-CLOSED → mainnet → master-only";;
|
*) echo "=> UNRECOGNIZED prefix => FAIL-CLOSED → mainnet → master-only";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
hr "done — raw payloads under $OUT_DIR/"
|
# --- 9. write .live-env -------------------------------------------------------
|
||||||
|
hr "9. write .live-env"
|
||||||
|
cat > "$LIVE_ENV" <<EOF
|
||||||
|
export KEYSAT_LIVE_BTCPAY_URL=$BASE
|
||||||
|
export KEYSAT_LIVE_BTCPAY_KEY=$ADMIN_KEY
|
||||||
|
export KEYSAT_LIVE_BTCPAY_STORE_REGTEST=$STORE_REGTEST
|
||||||
|
export KEYSAT_LIVE_BTCPAY_STORE_NOWALLET=$STORE_NOWALLET
|
||||||
|
export GATE_TOK_REGTEST=$GATE_TOK_REGTEST
|
||||||
|
export GATE_TOK_NOWALLET=$GATE_TOK_NOWALLET
|
||||||
|
EOF
|
||||||
|
echo "wrote $LIVE_ENV"
|
||||||
|
|
||||||
|
hr "done — raw payloads under $OUT_DIR/, credentials in $LIVE_ENV"
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
# Stage 2 setup: a sandbox Keysat daemon wired to the regtest BTCPay stack, a
|
# Stage 2 setup: a sandbox Keysat daemon wired to the regtest BTCPay stack, a
|
||||||
# scoped key that can BOTH onboard a catalog AND connect a payment provider
|
# scoped key that can BOTH onboard a catalog AND connect a payment provider
|
||||||
# (merchant-onboard + payment_providers:write), the docs corpus, and a sandbox
|
# (merchant-onboard + payment_providers:write), the docs corpus, and a sandbox
|
||||||
# app — then the agent brief for the buyer-pays journey.
|
# app — then the agent brief for the COMBINED journey: gate a paid product
|
||||||
|
# (define product + paid tier, integrate the SDK, prove the export is BLOCKED),
|
||||||
|
# then prove it end to end (connect BTCPay regtest, a buyer pays, and the
|
||||||
|
# PURCHASED license UNLOCKS the gated export).
|
||||||
#
|
#
|
||||||
# Networking: the daemon binds 0.0.0.0 and registers its BTCPay webhook via
|
# Networking: the daemon binds 0.0.0.0 and registers its BTCPay webhook via
|
||||||
# host.docker.internal so the BTCPay *container* can reach it on settle; the
|
# host.docker.internal so the BTCPay *container* can reach it on settle; the
|
||||||
@@ -67,32 +70,58 @@ DOCS_URL="$(state_get "$STATE" DOCS_URL)"; SANDBOX="$(state_get "$STATE" SANDBOX
|
|||||||
source "$STAGE2_DIR/btcpay-regtest/.live-env"
|
source "$STAGE2_DIR/btcpay-regtest/.live-env"
|
||||||
|
|
||||||
cat > "$RUN_DIR/AGENT_BRIEF.md" <<EOF
|
cat > "$RUN_DIR/AGENT_BRIEF.md" <<EOF
|
||||||
# Onboarding-tester brief — Keysat Stage 2 (agent connects BTCPay regtest + buyer pays)
|
# Onboarding-tester brief — Keysat combined journey (gate a paid product, then prove it with a real buyer payment)
|
||||||
|
|
||||||
You are a **fresh adopter**, following \`~/Projects/standards/guides/onboarding-tester.md\`.
|
You are a **fresh adopter**, following \`~/Projects/standards/guides/onboarding-tester.md\`.
|
||||||
Reach the goal using **only the docs corpus**. Never read Keysat source to unblock
|
Reach the goal using **only the docs corpus**. Never read Keysat's server or SDK source to
|
||||||
yourself — a gap in the docs is a finding.
|
unblock yourself — if the docs don't get you there, that is a finding.
|
||||||
|
|
||||||
## Goal (checkable end-state)
|
## Goal (checkable end-state)
|
||||||
Acting for a merchant on a **sandbox** Keysat instance, using a **scoped, non-master**
|
You are an operator selling a Next.js/TypeScript app. Do the **whole flow in the order an
|
||||||
API key (it carries \`payment_providers:write\`), and the published docs only:
|
operator actually works** — gate the paid feature first, then prove it end to end with a
|
||||||
|
real buyer payment. Use a **scoped, non-master** API key (it carries
|
||||||
|
\`payment_providers:write\`), a **sandbox** Keysat instance, and the published docs only:
|
||||||
|
|
||||||
1. **Connect a BTCPay payment provider** (this box's regtest BTCPay) to Keysat over the
|
1. **Define the product + a paid tier that grants an entitlement.** Register the product in
|
||||||
API — no master key, no human clicking in a browser. (You hold a BTCPay credential for
|
Keysat's catalog and add a **paid** policy/tier whose purchase grants a named
|
||||||
the regtest server, the way an operator delegating setup would hand one to you.)
|
**entitlement** (the thing your gate will check for). Note the entitlement key.
|
||||||
2. Create a product with a **paid** policy/tier.
|
2. **Integrate the SDK and gate the Pro export — verify the BLOCKED path FIRST.** Wire
|
||||||
3. Produce a **buyer checkout** for that product (a purchase invoice).
|
\`@keysat/licensing-client\` into your app so \`GET /api/export\` returns the CSV **only**
|
||||||
4. Confirm that paying the invoice issues a license (the harness will pay it on regtest if
|
when the caller holds a valid license carrying that entitlement. Then prove it is shut:
|
||||||
you cannot from the docs alone — note where the docs leave that to plumbing).
|
with **no** license and with a **tampered/invalid** license, \`/api/export\` returns a
|
||||||
|
**4xx, not the CSV**. (At this point no real license exists yet — that's expected.)
|
||||||
|
3. **Connect BTCPay (regtest) and drive a real buyer payment → license issued.** Connect
|
||||||
|
the regtest BTCPay to Keysat over the API (no master key, no browser — you hold a BTCPay
|
||||||
|
credential the way an operator delegating setup would hand you one). Produce a **buyer
|
||||||
|
checkout** for the paid product, then have the buyer pay it. The settled payment must
|
||||||
|
issue a **real, signed license** carrying the entitlement from step 1. (The harness will
|
||||||
|
pay the regtest invoice for you if the docs leave that last on-chain step to plumbing —
|
||||||
|
note where, but the *checkout* itself must come from the docs.)
|
||||||
|
4. **Paste the PURCHASED license into the app → verify the UNLOCKED path.** Feed that
|
||||||
|
purchased license to your app and confirm \`GET /api/export\` now returns the **CSV**.
|
||||||
|
This is the step that ties the two halves together: the license a *buyer's payment*
|
||||||
|
produced unlocks the feature your *gate* protects.
|
||||||
|
|
||||||
Success = a paid product whose purchase, once settled, yields a valid license — reached
|
Success = the same gate that was demonstrably shut in step 2 is opened in step 4 by a
|
||||||
from the docs alone, under a scoped key, with BTCPay connected by you.
|
license that a real (regtest) buyer payment produced in step 3 — reached from the docs
|
||||||
|
alone, under a scoped key, with BTCPay connected by you.
|
||||||
|
|
||||||
## Docs corpus (the ONLY how-to sources)
|
## Docs corpus (the ONLY how-to sources you may consult)
|
||||||
- Keysat docs site: **$DOCS_URL** (start at \`/agent.html\`, \`/integrate.html\`).
|
- Keysat docs site: **$DOCS_URL** — start at \`/integrate.html\` (SDK + gating) and
|
||||||
- Daemon OpenAPI: **$BASE_URL/v1/openapi.json**.
|
\`/agent.html\` (scoped-key + connect-BTCPay workflow); the whole site is in-corpus.
|
||||||
|
- Daemon OpenAPI: **$BASE_URL/v1/openapi.json** (unauthenticated; the docs point here).
|
||||||
|
- The npm package README for \`@keysat/licensing-client\` is in-corpus (\`npm view\` / the
|
||||||
|
package page).
|
||||||
|
|
||||||
## Credentials you were handed
|
## Your sandbox app (mutate ONLY this)
|
||||||
|
\`$SANDBOX\` — a pristine copy of the "Acme Reports" app whose **Pro export**
|
||||||
|
(\`GET /api/export\`) is currently ungated. Read its own \`README.md\` freely (it's your
|
||||||
|
app; it tells you nothing about Keysat). Deps are installed. Run it with \`npm run dev\`
|
||||||
|
(serves on http://localhost:4311). How the app learns the license key (env var, file,
|
||||||
|
header) is your call — pick what the docs suggest and note it. Put scratch under
|
||||||
|
\`/tmp/onboarding-tester/\`.
|
||||||
|
|
||||||
|
## Credentials you were handed (an operator delegating setup would hand you these)
|
||||||
- Keysat server: **$BASE_URL**
|
- Keysat server: **$BASE_URL**
|
||||||
- Scoped API key (merchant-onboard + payment_providers:write): **$SK**
|
- Scoped API key (merchant-onboard + payment_providers:write): **$SK**
|
||||||
- Regtest BTCPay server: **${KEYSAT_LIVE_BTCPAY_URL:-$BTCPAY_URL}**, store
|
- Regtest BTCPay server: **${KEYSAT_LIVE_BTCPAY_URL:-$BTCPAY_URL}**, store
|
||||||
@@ -102,7 +131,9 @@ from the docs alone, under a scoped key, with BTCPay connected by you.
|
|||||||
either an intended operator-only boundary (note it) or a doc gap (log it).
|
either an intended operator-only boundary (note it) or a doc gap (log it).
|
||||||
|
|
||||||
## Out of corpus (do not open)
|
## Out of corpus (do not open)
|
||||||
Anything under the Keysat source tree, migrations, tests, or this harness.
|
Anything under the Keysat source tree (\`$WORKSPACE/licensing-service-startos\`,
|
||||||
|
\`$WORKSPACE/licensing-client-*\`), migrations, tests, or this harness. Reading any of it
|
||||||
|
invalidates the run — say so if you do.
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
Write your friction report to \`$RUN_DIR/reports/friction.md\` AND return it as your final
|
Write your friction report to \`$RUN_DIR/reports/friction.md\` AND return it as your final
|
||||||
|
|||||||
Reference in New Issue
Block a user