Skip to content

fix(cli): retire the remaining "free API key" and "Authorization" payment copy - #414

Open
KishiTheMechanic wants to merge 2 commits into
mainfrom
fix/retire-free-key-and-authorization-copy
Open

KishiTheMechanic wants to merge 2 commits into
mainfrom
fix/retire-free-key-and-authorization-copy

Conversation

@KishiTheMechanic

@KishiTheMechanic KishiTheMechanic commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #413, which deliberately left four link-less "Get a free API key"
prose sites behind pending a copy ruling. The ruling is now recorded in
elsoul/vs2-app#13261: the EUR 5 charge is a one-time payment, captured in
full
— not a credit hold — and nothing the customer receives for it may be
called free.

Two words break that ruling wherever they reach a customer:

  • authorization names one regulated step of a card transaction, not the
    purchase. Used as the name of the whole purchase it reads as a hold that will
    be released, which is false here.
  • free attached to something the EUR 5 buys is the UCPD Annex I point 20
    shape — per se, no materiality test.

Sites changed

CLI strings (commit 242c8ecc)

File Before After
cli/src/check/index.ts:59 Get a free API key and configure it first. Get your API key at ${ERPC_DASHBOARD_URL} and configure it first.
cli/src/check/index.ts:344 Get a free API key and configure ~/.slv/api.yml, … Get your API key at ${ERPC_DASHBOARD_URL} and configure ~/.slv/api.yml, …
cli/src/signup/index.ts:15 complete the €5 payment authorization complete the one-time €5 payment
cli/src/ai/console/providers/slv.ts:139 Complete Authorization (€5) to receive 100,000 free AI tokens Complete the one-time €5 payment to receive 100,000 AI tokens
cli/src/ai/product/productAction.ts:80 Complete Authorization to receive 100,000 free AI tokens and … Complete the one-time payment to receive 100,000 AI tokens and …
cli/src/ai/product/productAction.ts:197 formatLink('Authorization', …) formatLink('Payment', …)
oss-skills/slv-benchmark/SKILL.md:65 + dist/ mirror get a free API key and configure it first. get their API key at https://dashboard.erpc.global and configure it first.

The check phrasing is not new: Get your API key: https://dashboard.erpc.global
already ships in cli/src/ai/onboard/onboardAction.ts across 5 locales. This
converges four stray sites onto the string already in the binary.

The 100,000 AI tokens are real and are what the EUR 5 buys — the checkout path
applyAuthRegistrationCheckoutSession.ts calls grantSlvAiFreeAuth, granting
SLV_AI_TOKENS.freeAuth = 100_000. So the grant stays in the copy; only the
word free goes, because the tokens are paid for.

The money-button label matters on its own: the sibling branch one block up
already labels its equivalent Purchase, so Authorization was the odd one out
in the same list.

Shipped skill documentation (commit e1cc94bb)

The first review round on this PR returned FAIL (see Review record below):
the CLI copy above was repaired, but 15 further sites in the skill
documentation still told readers the API key was free
. These docs ship to
clawhub, so the claim would have gone out with the release.

Each of these lines carried two defects — a price claim, and a destination
that cannot issue a key (erpc.global is the marketing site; keys are issued at
dashboard.erpc.global). Both are fixed.

Before After Sites
ERPC API keys are free to obtain at https://erpc.global — **recommended for full monitoring**. Get an ERPC API key at https://dashboard.erpc.global — **recommended for full monitoring**. slv-validator, slv-rpc, slv-grpc-geyser AGENT.md ×2 trees = 6
ERPC API keys are free at https://erpc.global — enables full slot sync monitoring … Get an ERPC API key at https://dashboard.erpc.global — enables full slot sync monitoring … slv-validator, slv-rpc, slv-grpc-geyser SKILL.md ×2 trees = 6
You'll need an ERPC API key first. Get a free one and add it to ~/.slv/api.yml: You'll need an ERPC API key first. Get one at https://dashboard.erpc.global and add it to ~/.slv/api.yml: slv-benchmark/AGENT.md ×2 trees = 2
instruct the user to get a free key and configure it first. instruct the user to get their API key at https://dashboard.erpc.global and configure it first. oss-skills/slv-rpc/SKILL.md = 1

The last row deliberately mirrors the phrasing already landed at
slv-benchmark/SKILL.md:65 in 242c8ecc, so the two sibling agent
instructions stay worded the same.

On the dist/ mirror: dist/oss-skills/** is a checked-in build artifact.
build-skill-oss.sh copies SKILL.md/AGENT.md verbatim from oss-skills/**,
but the committed mirror has already drifted from its source (e.g.
slv-rpc/SKILL.md differs by 191 lines), so regenerating would produce large
unrelated churn. Both trees are therefore edited by hand in the same commit,
exactly as 242c8ecc did.

Not touched (deliberate)

  • free attached to the erpc.global subdomain and to egress
    (onboardAction.ts, storage/product/productAction.ts + i18n mirrors) —
    an included feature and a pricing term, not a price claim about the API key.
  • Every internal identifier (authorizationLink, AuthState,
    isSecureAuthorizationProduct) and every HTTP Authorization header — never
    customer-visible, and correct as a header name.
  • Secure Authorization as the Stripe product name stays; only the
    imperative "Complete Authorization" as a call to action is replaced.

Receipts

All figures below were re-measured on the current head e1cc94bb.
BEFORE is measured at c76986f5 (the merge base with main).

Doc price claim — predicate
API key[s]? (are|is) free|free (one|key)|get a free|free to obtain
over *.md *.ts *.js *.json *.yml *.j2, excluding .git:

count
BEFORE (head 242c8ecc) 15
AFTER (head e1cc94bb) 0
Liveness control: same predicate at parent c76986f5 17

The control matters: it proves the 0 is a real clearance rather than a predicate
that stopped matching. It returns 17 because it still sees the two
slv-benchmark/SKILL.md rows that 242c8ecc had already cleared.

Widened gratis sweep (per reviewer request, so the measured population
matches the scope of the claim) — free/gratis/complimentary/no cost/
no charge/zero cost/$0/EUR 0 over the customer-visible doc trees
(oss-skills, dist/oss-skills, docs, README.md): 4 rows, none a claim
about the API key
— free -m and "free port" (shell command references), and
the WireGuard app being free software (a true third-party fact).

CLI strings — repo-wide, excluding .git:

Predicate BEFORE AFTER
free api key (-i) 4 0
payment authorization (-i) 1 0
Complete Authorization 2 0
free AI tokens 2 0

Positive control on the same predicate shape (proves it can return non-zero):
one-time = 10, Get your API key = 9, dashboard.erpc.global = 34.

Build / test:

  • deno check cli/src/index.ts: 12 errors before, 12 after, identical error
    set
    (diff of the sorted error sets returns rc=0). The 12 are pre-existing
    on main and unrelated to this PR; the e1cc94bb commit touches .md files
    only, so no code path changed.
  • cd cli && deno test -A --config=deno.json test: 124 passed, 0 failed
    (rc=0) — the same command CI runs.
  • deno.lock is not in this PR: the ERPC_DASHBOARD_URL import into
    check/index.ts adds no dependency.

Review record

An earlier revision of this description claimed this PR had passed the EN
payment-register review. That was wrong, and it is corrected here. The actual
sequence:

Round 1 — rocky (stablecoin/payments register owner): FAIL. Three findings:

  1. (N3, veto-level) 15 sites in the shipped skill docs still claimed the ERPC
    API key was free. Fixed in e1cc94bb — this is the substance of the
    second commit.
  2. (N1) cli/src/ai/product/productAction.ts shows the "Complete the one-time
    payment" description whenever the authorization state is not authorized,
    including the unknown state. Out of scope for this PR — see below.
  3. (N2) The Stripe product name Secure Authorization still reaches the
    customer's Plan: line. Out of scope for this PR — brand/product naming,
    owned elsewhere.

Round 2 — rocky: PASS. Verdict, verbatim on the substantive question:

All four AFTER shapes are clean. No replacement strings required.

The BEFORE copy … described the product itself as free while a EUR 5 payment
is a condition of issuance. That is squarely the Annex I point 20 shape — a
blacklisted practice, unfair in all circumstances, with no average-consumer or
materiality test available to argue it down. That is why round 1 was correctly
a FAIL and not a style note.

The AFTER copy removes the descriptor rather than qualifying it, which is the
right repair.

On whether these docs must also state the EUR 5 price, rather than only routing
to the issuance point:

Omitting the price is acceptable at these sites, and it is the better choice
than stating it at each site. … A price written in fifteen places will not be
updated in fifteen places. A stale price in public documentation is a worse
defect than silence, because it is an affirmative false statement about price
rather than an omission. … state the cost once, at one canonical site, and
route from everywhere else — not zero, not fifteen.

The reviewer recorded one condition that is not a finding against this PR
and does not block it: this review certifies the doc strings only, not the
checkout surface at dashboard.erpc.global, which is where the actual
pre-contractual disclosure obligation sits (CRD Art 6(1)(e) / Art 8(2)). Whether
the EUR 5, its one-time nature and its refund treatment are disclosed clearly
and prominently there is owned by that surface, and is worth a separate look.

Findings deferred out of this PR

N1 — productAction.ts authorization-state asymmetry. Reproduced, and it
pre-dates this PR: both lines are byte-identical at the merge base
c76986f5, so this PR neither introduced nor worsened it.

The mechanism is not quite as first reported. The description string at line 80
has no state check at all — it is selected purely by product name. The
asymmetry is between two other lines:

  • productAction.ts:143 — if (authorizationStatus.state !== 'authorized') return true
    keeps the Secure Authorization product visible for all three states.
  • productAction.ts:194 — authorizationStatus.state === 'unauthorized'
    renders the Payment link for one state only.

'unknown' is reachable: cli/src/ai/authorization.ts returns it on a null or
malformed payload (:121), a non-OK HTTP response (:149), and a thrown fetch
(:160). So when the authorization endpoint is merely unreachable, the user is
shown an instruction to "Complete the one-time payment" with no payment link
to act on
— and a customer who is in fact already authorized is told to pay
again. Worth a follow-up; not a copy defect, and not this PR's to fix.

N2 — Stripe product name. Secure Authorization still reaches the customer
as the Plan: value. That is a product/brand naming decision, not a register
question, and is owned outside this repository.

Not in this PR

No release is cut here and sh/install VERSION is untouched.

Note for whoever ships this: ValidatorsDAO/slv cannot currently produce a
release. Both the test job (for non-PR events) and the remote-build job
target the self-hosted slv-bm-runners scale set, which has picked up no job
since 2026-07-03; the #413 merge run 34401455125 has been queued since
2026-09-09. A tag push today would queue and never build. That is tracked
separately, and is why no tag accompanies this PR.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com
Co-Authored-By: Claude Opus 5 noreply@anthropic.com

KishiTheMechanic and others added 2 commits September 10, 2026 14:13
…ment copy

#413 left four link-less "Get a free API key" prose sites behind, pending a
copy ruling. That ruling is now recorded (elsoul/vs2-app#13261): the EUR 5
charge is a one-time payment that is captured in full, not a credit hold, and
nothing the customer receives for it may be described as free.

Two words are wrong against that ruling wherever they reach a customer:

- "authorization" names one regulated step of a card transaction, not the
  purchase. Using it as the name of the whole purchase reads as a hold that
  will be released, which is false here.
- "free" attached to something the EUR 5 buys is the UCPD Annex I point 20
  shape (per se, no materiality test).

Changes, scoped to customer-visible strings:

- check: both "Get a free API key" sites now point at the ERPC dashboard,
  matching the phrasing already shipped in onboard (5 locales).
- signup: "complete the EUR 5 payment authorization" -> "complete the
  one-time EUR 5 payment".
- ai/console: "Complete Authorization (EUR 5) to receive 100,000 free AI
  tokens" -> "Complete the one-time EUR 5 payment to receive 100,000 AI
  tokens". The 100,000 grant is real and is what the EUR 5 buys, so the
  tokens are not free.
- ai/product: the same sentence in the hardcoded product description, and
  the money-button label "Authorization" -> "Payment" (the sibling branch
  already labels its equivalent "Purchase").
- slv-benchmark SKILL.md + its dist mirror: same substitution in prose.

Not touched: "free" attached to the erpc.global subdomain and to egress,
which are included features and a pricing term rather than a price claim
about the API key; and every internal identifier and HTTP Authorization
header, none of which is customer-visible.

No release is cut here and sh/install VERSION is untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ed skill docs

The first review round on this PR (rocky, EN payment-register) returned FAIL:
the CLI-side copy was repaired in 242c8ec, but 15 sites in the skill
documentation still told readers the ERPC API key was free. Those docs ship to
clawhub, so the false claim would have gone out with the release.

Against the recorded ruling (elsoul/vs2-app#13261) the claim is simply untrue:
an API key is issued only after a EUR 5 registration payment that is captured
in full and is not refundable. There is no free tier, so "free" attached to the
key is the UCPD Annex I point 20 shape — blacklisted per se, with no
materiality test available.

The repair drops the price adjective rather than qualifying it, and points at
the issuance endpoint instead of the marketing site:

- "ERPC API keys are free to obtain at https://erpc.global"
  -> "Get an ERPC API key at https://dashboard.erpc.global"   (3 AGENT.md + 3 dist)
- "ERPC API keys are free at https://erpc.global"
  -> "Get an ERPC API key at https://dashboard.erpc.global"   (3 SKILL.md + 3 dist)
- "Get a free one and add it to ~/.slv/api.yml"
  -> "Get one at https://dashboard.erpc.global and add it"    (benchmark AGENT.md + dist)
- "instruct the user to get a free key"
  -> "instruct the user to get their API key at https://dashboard.erpc.global"
  (slv-rpc SKILL.md; mirrors the phrasing already landed at slv-benchmark
  SKILL.md:65 in 242c8ec)

These lines carried two defects each: a price claim, and a destination that
cannot issue a key. Both are fixed.

dist/oss-skills/** is a checked-in build mirror. build-skill-oss.sh copies
SKILL.md/AGENT.md verbatim from oss-skills/**, but the committed mirror has
drifted from its source, so regenerating would produce unrelated churn. Both
trees are therefore edited by hand in this commit, as 242c8ec did.

Measured, predicate
`API key[s]? (are|is) free|free (one|key)|get a free|free to obtain`
over *.md *.ts *.js *.json *.yml *.j2:
  BEFORE 15 rows -> AFTER 0 rows.
Predicate liveness: the same predicate still returns 17 rows against parent
c76986f, so the 0 is a real clearance and not a broken pattern.
A widened gratis sweep (free / gratis / complimentary / no cost / no charge /
zero cost / $0 / EUR 0) over the customer-visible doc trees returns 4 rows,
none of them a claim about the API key: two `free -m` / "free port" command
references, and the WireGuard app being free software (a third-party fact).

deno check cli/src/index.ts: 12 pre-existing errors before and after, identical
error set. No code paths touched.

No release is cut here and sh/install VERSION is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant