Dosu LogoDosu Logo
Ask
Join our Discord
Organization avatar
HashHivePublic
EvilBit Labs LLC
DocumentsHashHive
0001-agent-enrollment-two-token-exchange
0001-agent-enrollment-two-token-exchange
Type
External
Status
Published
Created
Jul 18, 2026
Updated
Jul 18, 2026
Source
docs/adr/0001-agent-enrollment-two-token-exchange.md

ADR-0001: Agent enrollment via two-token exchange#

Date: 2026-06-18
Status: accepted
Deciders: Project owner (@unclesp1d3r), AI pair (Claude Code)

Context#

HashHive had no way to register a new agent: agent rows were only ever
inserted by the legacy migrate-data.ts script, and the per-agent
bearer-token rotation endpoint requires an agent that already exists. The
dashboard onboarding even told operators to "grab a token from the agents
page," which had no such affordance. We need a mechanism for an admin to
authorize a brand-new agent on a private/air-gapped LAN (#233, #114).

Decision#

An admin mints a short, human-typeable enrollment token
(etk_<id>_<word-phrase>, project-scoped, one-time or reusable, optional
expiry, revocable). The agent presents it once to an anonymous
POST /api/v1/agent/enroll endpoint, which atomically validates and
consumes it and issues the agent its long-lived per-agent bearer token
(the existing agt_<id>_<random> credential). The human never types the
bearer token. Enrollment is idempotent on an agent-supplied stable
clientId. Both enrollment secret and bearer token are bcrypt-hashed (cost
12); the raw values are shown once and never persisted.

Alternatives Considered#

Alternative 1: Single pre-shared bearer token typed into the agent#

  • Pros: simplest; no new endpoint or table.
  • Cons: the 43-char base64url bearer token is not human-typeable; no
    lifecycle (expiry/revocation/usage caps); no batch enrollment.
  • Why not: fails the "typeable, operator-friendly, revocable" goal and
    can't support bringing up a rack of rigs at once.

Alternative 2: Fast hash (SHA-256/HMAC) for the enrollment secret#

  • Pros: cheaper verify; avoids bcrypt CPU on an unauthenticated endpoint.
  • Why not: a typeable token is necessarily low-entropy (a word-phrase),
    so bcrypt's slowness is protective, not waste. The id routing hint means
    an unknown id triggers no hash at all, so the unauthenticated-DoS concern is
    moot. Keeping bcrypt-12 also preserves the codebase's single-hash unification.

Alternative 3: Per-IP rate limiting on the enroll endpoint#

  • Pros: bounds brute-force/abuse on an anonymous endpoint.
  • Why not: a reusable token's whole purpose is batch enrollment — many
    rigs behind one NAT'd egress IP — which naive per-IP limiting would throttle.
    Abuse is already bounded by atomic per-token usage caps and the cheap-reject
    (unknown id → no row → no bcrypt). If backpressure is ever needed, the
    intended shape is an optional slow-down response header, not a request limiter.

Consequences#

Positive#

  • Honest first-run: the onboarding arc now lands on real affordances.
  • Reuses the existing agt_ bearer infrastructure unchanged.
  • Project-scoped, revocable, expiring, usage-capped credentials with an audit
    trail (revoked_at timestamp, use_count).
  • Idempotent retry: a dropped enroll response doesn't mint duplicate agents.

Negative#

  • New unauthenticated endpoint that creates agents (mitigated: requires a valid
    project-scoped secret; atomic consumption caps blast radius).
  • A second credential format (etk_) alongside agt_/cst_.

Risks#

  • DB-level atomicity of the guarded consume + partial unique index is currently
    proven by reasoning + decision-logic tests, not a real-DB concurrency test;
    the planned testcontainers work will close this.
  • The rebuilt Go agent must conform to the /enroll contract + stable clientId
    (owned by this repo's contract; tracked separately).
Documents
.impeccable
0001-agent-enrollment-two-token-exchange
0002-docker-supervision-over-pm2
0003-bind-agent-to-enrollment-token
0004-agent-api-revisable-until-1.0
0005-version-public-api-after-1.0
0006-seaweedfs-over-minio
0007-three-api-surfaces
0008-betterauth-over-handrolled-jwt
0009-postgres-task-store-bullmq-orchestration
0010-schema-first-drizzle-zod
0011-pull-advisory-notify-task-distribution
0012-in-memory-websocket-v1-redis-deferred
0013-route-as-spec-hono-zod-openapi
0014-turborepo-bun-over-nx
0015-append-only-telemetry-timescaledb-rrd
0016-server-fanout-bus
0017-adaptive-task-sizing-lease
0018-real-db-integration-test-lane
0019-campaign-archiving-immutable-lifecycle
155-ac-traceability-matrix
155-task-distribution-assignment-spec
156-ac-traceability-matrix
156-object-storage-file-management-spec
160-login-project-selection-ui
2026-05-25-websocket-realtime-infrastructure-requirements
2026-05-29-dashboard-stats-and-read-endpoint-contract-requirements
2026-05-31-openapi-auto-generation-migration-requirements
2026-06-08-results-analysis-export-ui-requirements
2026-06-19-task-distribution-architecture
2026-06-20-campaign-archiving-requirements
2026-06-30-hash-export-import-search-requirements
227-spa-deep-link-refresh-redirect-spec
94-p0-cracker-binary-management-agent-auto-update
96-keyspace-task-distribution-spec
97-task-preemption-spec
99-attack-complexity-state-machine-spec
AGENTS
AGENTS
AI_POLICY
AI_POLICY
ARCHITECTURE
ARCHITECTURE
Agent_Authentication_&_Authorization
Agent_Heartbeat_&_Error_Handling
Agent_List_&_Detail_UI
Base Model Schema System
BullMQ_Queue_Architecture_&_Redis_Integration
CLAUDE
CONTRIBUTING
CONTRIBUTING
Campaign_Creation_Wizard_UI
Campaign_List_&_Detail_UI
Campaign_Orchestration_API
Code Quality Automation with Husky and Kiro Hooks
Core_Flows__HashHive_User_Journeys
Dashboard_&_Real-Time_Monitoring_UI
Dashboard_Stats_API_Endpoint_(GET__api_v1_dashboard_stats)
Development Workflow and Tooling Integration
ESLint and Formatting Configuration
Epic_Brief__HashHive_-_Modern_Distributed_Password_Cracking_Platform
GOTCHAS
GOTCHAS
Health Check Endpoint Enhancements
Login_&_Project_Selection_UI
MERN_proposal
Mixed Hash Type Detection & Automatic Campaign Splitting (#202)
MongoDB Connection Management
Object_Storage_&_File_Management
Project_Selection_&_User_Authentication_API
README
README
README
README
README
README
Real-Time_Events_&_WebSocket_Infrastructure
Resource_Management_API
Resource_Management_UI
Results_API_&_CSV_Export_(Dashboard_Backend)
Results_Analysis_&_Export_UI
SECURITY
SECURITY
STRATEGY
Task_Distribution_&_Assignment
Tech_Plan__HashHive_Architecture
Testing Strategy for Database and Models
advanced_task_scheduler
agent
agent-resource-protocol
agent-server-collaboration-vision
agent-token-rotation
agent_notes
agent_sync_extensions
agent_v1_contract_violations
allow_hybrid_hash_lists
attack
attack_notes
authentication_cleanup
back-compat-credential-format-migration
barrel-plus-subdir-service-split
brute_force_attack_editor
bullmq-jobid-dedup-requires-eviction
bun-test-mock-module-import-order
campaign_list_view
campaign_notes
campaigns
ci_check.prompt
code_review.prompt
contract-test-mocks-mirror-service-not-schema
copilot-instructions
core_algorithm_implementation_guide
crackable_uploads_plan
dashboard-read-endpoint-contract
dashboard-ux
default_config_suggestions
development
development
feat-agent-list-detail-ui
feat-betterauth-project-selection-rbac
feat-campaign-list-detail-ui
feat-masklist-keyspace
form-submit-payload-null-checks-2026-05-19
full_testing_architecture
getbytext-collision-waitfor-timeout
hard_password_attack_strategies
hash_hive_gap_analysis
health_status_screen
implementation_plan
learned_rules_parser_plan
llmstxt.prompt
markov_autogen_plan
new_dictionary_attack_editor
new_mask_attack_editor
openldap-testcontainer-bootstrap-race
performance_tuning.prompt
phase-1-core-infrastructure
phase-2-api-implementation
phase-2-api-implementation-part-1
phase-2-api-implementation-part-2
phase-2-api-implementation-part-3
phase-2b-resource-management
phase-2c-refactor-cleanup
phase-3-e2e-test-coverage-plan
phase-3-step-1
phase-3-step-2
phase-3-step-3
phase-3-step-4
phase-3-step-5
phase-3-web-ui-foundation
phase-4-containerization-deployment
phase-5-task-distribution
phase-6-monitoring-testing-documentation
playwright-shared-seed-user-last-project-id-flake
playwright-visual-baselines
pre-prod-wire-rename-and-schema-version-bump
previous_password_dictionary_attack_editor
projection-narrows-zrecord-wire-shape
psycopg_migration
radix-roving-focus-preventdefault-breaks-keyboard-nav
reactflow-position-by-array-index-2026-05-19
rule_explanation
salvage_templates
security_hardening.prompt
shadcn-radix-non-destructive-add-on-tailwind-v4
shared-zod-openapi-wire-contract-mirror-2026-05-25
spa_to_ssr
summary
template
test-coverage-plan
testcontainers_minio_support
testing
testing
user_flows_notes
v2-upgrade-overview
v2_agent_api_removal
work_next_task.prompt