Declared actors: opt-in human/agent identity on the tracker - #339
Merged
Merged
Conversation
ThreatCrush Security Scan49 finding(s) HIGH/CRITICAL: 2 | MEDIUM: 32 | LOW: 15
Snippets are redacted; ThreatCrush never prints matched credential material. |
Nothing on the wire separates a person from an agent driving a real
browser, so visitors may now say who they are. An account registers
actors (email, name, kind human|agent, optional human operator for an
agent) and mints cpa_ tokens per browser or agent. The beacon carries the
token by the Crawlproof-Actor header (headless agents) or the body, from
localStorage set by a ?crp_actor= link or crawlproof('actor', token).
A bare email is never accepted.
Built for people gaming it:
- one-way trust: a declared agent is believed (bot:declared, and the
visitor rollup counts it as a bot); a declared human never overrides
bot detection and a mismatch counts as a contradiction on the actor
- tokens hashed with the API pepper under an "actor:" domain
- a verified address can be claimed by one account only; the owner's
login is verified on creation, anything else by an emailed link
- names are private to the owner unless made public; other site owners
see per-kind counts
The tracker stays cookieless (credentials: 'omit' is pinned by a
contract test), so there is no cookie channel; the dashboard's
"Declare this browser" hands out one ?crp_actor= link per tracked
project plus a bookmarklet for other sites.
Surfaces: /api/tracker/v1/actors (+ tokens, verify), declared totals in
/api/tracker/v1/stats, `crawlproof actors`, MCP list_actors / add_actor
/ mint_actor_token, Settings -> Declared actors, docs section.
Migration 20261004120000_tracker_declared_actors.sql: apply by hand
after merge. Until then the ingest treats every token as undeclared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ralyodio
force-pushed
the
worktree-declared-actors
branch
from
October 4, 2026 13:41
2e3a332 to
733d4e0
Compare
ralyodio
added a commit
that referenced
this pull request
Oct 4, 2026
#339 added `actors` to the in-repo CLI (cli/index.ts) only. The `crawlproof` on PATH is the published @profullstack/crawlproof (packages/cli, 0.3.0), which answered 'unknown command: actors'. The command now lives in lib/tracker/actorsCli.ts and both CLIs call it, the same way lib/emailTracking/cli is shared, so they cannot drift. Package bumped to 0.4.0. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Nothing on the wire separates a person from an agent driving a real browser. The tracker guesses from the user agent and the scripted cap, so an undeclared agent in Chrome reads as a human visit. This adds the honest half: an opt-in, self-reported declaration, so we can track internally who is who (first two actors:
anthony@profullstack.comhuman,riotcoder@profullstack.comagent operated by Anthony) and offer it to customers.How it works
human|agent, optional human operator for an agent) and mintscpa_tokens per browser or agent. The token is the credential; a bare email is never accepted.Crawlproof-Actor: cpa_…header (PlaywrightextraHTTPHeaders, Puppeteer)?crp_actor=cpa_…(stripped from the URL) orcrawlproof('actor', token)credentials: 'omit'is pinned bytests/contract/stats-js.test.ts. "Declare this browser" in the dashboard hands out one?crp_actor=link per tracked project plus a bookmarklet.Built for people gaming it
applyDeclaration): a declared agent is believed (bot:declared, and the visitor rollup counts it as a bot). A declared human never overrides bot detection; a mismatch (bot UA or scripted cap) is counted as a contradiction on that actor.actor:domain), revocable one by one, looked up at most once a minute per process.Surfaces
GET/POST /api/tracker/v1/actors,PATCH/DELETE /actors/:id,POST/DELETE /actors/:id/tokens,GET /actors/:id/verify. Bearercrp_or session./api/tracker/v1/statsgainsdeclared(per-kind totals + your own actors);crawlproof statsprints a "Declared (self-reported)" block.crawlproof actors list|add|token|revokelist_actors,add_actor,mint_actor_tokenMigration
supabase/migrations/20261004120000_tracker_declared_actors.sql: apply by hand after merge (deploys don't run migrations). Safe in either order: before it, token lookups fail and every beacon is undeclared,actor_idis only written when an actor resolved, anddeclaredisnull.Checks
tsc --noEmitclean;vitest run217 files / 2742 tests pass (24 new intests/tracker-declared-actors.test.ts, including route-level: declared agent in stock Chrome →p_kind: bot,bot:declared,actor_idon the raw event; declared human with GPTBot UA → stays bot, contradiction).authenticatedcannot calltracker_touch_actor.🤖 Generated with Claude Code