revenue-truth — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited revenue-truth (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
On 2026-05-26 the CTO answered "why didn't we make money?" three times in a row by quoting planning documents and stale March snapshots as if they were current telemetry. The CEO had to push back with "are you sure?" three times before the CTO admitted he had never queried the live billing endpoint. This skill exists so that never happens again.
The first version of this skill (written same day) referenced an env var named THUMBGATE_ADMIN_KEY that does not exist anywhere in the codebase — a name I invented without grepping. The CEO caught it with another "are you sure?". The skill now defers to the canonical scripts/revenue-status.js pipeline so it cannot drift again.
Before claiming any revenue, visitor, conversion, or funnel number, you MUST run `node scripts/revenue-status.js` and quote its output. If no credentials are configured, say so — do not fall back to planning docs.
node scripts/revenue-status.jsReads credentials in this exact priority order (same as scripts/operational-summary.js and scripts/operational-dashboard.js):
$THUMBGATE_OPERATOR_KEY — read-only billing-summary access, recommended for agents~/.config/thumbgate/operator.json — created by node bin/cli.js billing:setup$THUMBGATE_API_KEY — full admin, only when operator unavailableOutput sections (quote these directly, never paraphrase):
Source: — hosted-billing-summary (live) vs local-fallback (no creds)Today / 30d / Lifetime: — visitors, pageViews, checkoutStarts, paidOrders, bookedRevenue30d attribution coverage — channel telemetry qualityGaps: — exact phrases describing what's missingIf you need a single field rather than the full report, query directly:
KEY="${THUMBGATE_OPERATOR_KEY:-${THUMBGATE_API_KEY:-}}"
[ -z "$KEY" ] && { echo "no operator/api key configured"; exit 1; }
curl -fsS \
-H "Authorization: Bearer ${KEY}" \
"https://thumbgate-production.up.railway.app/v1/billing/summary?window=30d" \
| jq '{
window,
booked_cents: .revenue.bookedRevenueCents,
paid_orders: .revenue.paidOrders,
checkout_starts: .funnel.checkoutStarts,
visitors: .funnel.uniqueVisitors,
acquisition: .funnel.acquisitionBySource
}'curl -fsS https://api.stripe.com/v1/charges?limit=20 \
-u "${STRIPE_SECRET_KEY:?set STRIPE_SECRET_KEY in env}:" \
| jq '[.data[] | select(.paid==true and .refunded==false)
| {amount, created, description, customer}]
| {count: length, total_cents: (map(.amount) | add)}'Note the var is STRIPE_SECRET_KEY (matches src/api/server.js:2597), not STRIPE_SECRET. If the two sources disagree, `/v1/billing/summary` is truth for booked-revenue claims; Stripe disagreement is an attribution bug worth filing.
| Source | Truth? | Notes |
|---|---|---|
scripts/revenue-status.js output where Source: hosted-billing-summary | YES | Backed by Stripe-reconciled ledger |
scripts/revenue-status.js output where Source: local-fallback | NO | Means no key configured; numbers are zero-state local |
Stripe API /v1/charges | YES | Cross-check only |
reports/gtm/*/operator-close-packet.md numbers labeled "revenue plan" | NO | These are forecasts |
docs/VERIFICATION_EVIDENCE.md snapshot dates | YES at that date, NO as current | Always check the date in the section heading |
docs/COMMERCIAL_TRUTH.md cumulative line | YES as of file's "Updated:" date | Stale if git log -1 -- docs/COMMERCIAL_TRUTH.md is >7 days old |
State this exactly:
"No operator key in this session's env and no~/.config/thumbgate/operator.jsonon this container.scripts/revenue-status.jsreportsSource: local-fallback. The last verified production snapshot in the repo is<date>: <numbers>fromdocs/VERIFICATION_EVIDENCE.md. Any number labeled '30d' in operator packets without a corresponding live curl is a forecast, not measured traffic."
Then offer one specific resolution:
"To unlock live numbers for this session AND future sessions: paste aTHUMBGATE_OPERATOR_KEYvalue into the harness env (the agent container's env, not Railway's server env). Generate one withnode bin/cli.js billing:setupon a machine where the admin key is already configured, or copy the existing operator key from Railway's variables."
Then stop. Do not speculate. Do not quote forecasts as actuals.
When booked revenue is low, also pull acquisition breakdown:
KEY="${THUMBGATE_OPERATOR_KEY:-${THUMBGATE_API_KEY:-}}"
curl -fsS \
-H "Authorization: Bearer ${KEY}" \
"https://thumbgate-production.up.railway.app/v1/billing/summary?window=30d" \
| jq '.funnel.acquisitionBySource'This tells you whether traffic came from channels we actually posted to (Bluesky, Threads, Reddit) or from background ai_search / direct. Channel mismatch is usually the real story.
Use the wrapper, not the raw script:
npm run revenue:truth # or: bash bin/revenue-truth.shThe wrapper handles three branches:
Source: hosted-billing-summary.~/.config/thumbgate/operator.json exists but no longer authenticates against Railway after a rotation). Runs the pipeline, then prints a loud WARNING — configured operator key authenticated against the LOCAL fallback block with the exact fix command. This is what you want to see when describing the 401 to the CEO instead of guessing.$CI, $CODESPACES, $GITHUB_ACTIONS, $CLAUDE_CODE_REMOTE, or /home/user/... Linux container path). Prints a one-paragraph "revenue truth is local-only by design, run from your laptop, do NOT paste the key here" message and exits 0. This is the expected posture for cloud sessions — not a bug, not a blocker, not something to keep reporting back to the CEO.The wrapper refuses to accept the operator key as a CLI argument (exits 64). Pasting the key on the command line would leak it to shell history; pasting it into the Claude transcript would leak it to the model context. Per CLAUDE.md hard-block rule #2.
Anti-pattern this section exists to prevent: an agent in a cloud session running node scripts/revenue-status.js directly, hitting 401, then repeatedly telling the CEO "I can't see hosted revenue" across multiple turns as if it were news. After the first observation, route to npm run revenue:truth so the diagnostic message is the response, not a back-and-forth.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.