Skip to content

bawolf/hallpass

Hallpass

Delegated agent identity and message verification. hallpass.org

Hallpass answers one question for any verifier:

Did this exact message come from an agent that is currently delegated by this user?

How it works

  1. A person creates a username account at hallpass.org
  2. They add public attestations (domain DNS, X post, GitHub gist, Bluesky handle)
  3. An agent registers a public key and proves possession
  4. The person delegates to that agent
  5. The agent signs messages with its own key
  6. Anyone can verify the username, delegation chain, and message signature together

The verification result is message-specific, not just profile-level.

Packages

Package Language Path
@hallpass/sdk TypeScript packages/sdk-typescript
@hallpass/cli TypeScript packages/cli
hallpass-sdk Python packages/sdk-python
hallpass_sdk Elixir packages/sdk-elixir

Quick start (CLI)

# 1. Log in (or register at hallpass.org first)
npx @hallpass/cli auth login

# 2. Register an agent under your profile
npx @hallpass/cli agent register my-agent

# 3. Sign a message as the delegated agent
npx @hallpass/cli message sign --username alice --body "Invoice #1234 approved"

# 4. Anyone can verify it
npx @hallpass/cli message verify bundle.json

Quick start (TypeScript)

import {
  createClient,
  createMessageProofBundle,
  loadAgentKeyMaterial,
  verifyMessageBundle,
} from '@hallpass/sdk';

const client = createClient({ baseUrl: 'https://hallpass.org/api/v1' });
const keyMaterial = await loadAgentKeyMaterial();

// Sign a message as a delegated agent
const bundle = createMessageProofBundle({
  principalUsername: 'alice',
  body: 'Invoice #1234 approved',
  authorLabel: 'Billing Agent',
  keyMaterial,
});

// Anyone can verify: identity, delegation chain, and signature
const { data } = await verifyMessageBundle({ client, body: bundle });
console.log(data.overall_decision); // "verified"

Quick start (Python)

from hallpass_sdk import HallpassClient, generate_agent_key_material, create_message_proof_bundle

key_material = generate_agent_key_material()

bundle = create_message_proof_bundle(
    principal_username="alice",
    body="Invoice #1234 approved",
    author_label="Billing Agent",
    key_material=key_material,
)

client = HallpassClient()
result = client.verify_message(bundle)

Self-hosting

Hallpass is a Phoenix (Elixir) application with a React frontend.

Requirements

  • Elixir 1.17+ / OTP 27+
  • PostgreSQL 15+
  • Node.js 22+ / pnpm

Local development

# Install dependencies
mix deps.get
cd assets && pnpm install && cd ..

# Create and migrate the database
mix ecto.setup

# Start the server
mix phx.server

The app will be available at http://localhost:4000.

Environment variables

Variable Required Description
DATABASE_URL prod PostgreSQL connection string
SECRET_KEY_BASE prod Phoenix secret (generate with mix phx.gen.secret)
KEY_ENCRYPTION_KEY prod 32-byte base64 key for encrypting service-managed private keys
PHX_HOST prod Public hostname (e.g. hallpass.org)
GCS_BUCKET optional GCP Cloud Storage bucket for avatar uploads
GCS_SERVICE_ACCOUNT_JSON optional GCP service account JSON key
GITHUB_TOKEN optional GitHub API token for higher rate limits on gist verification

Deploy to Fly.io

fly launch --copy-config
fly secrets set SECRET_KEY_BASE="$(mix phx.gen.secret)"
fly secrets set KEY_ENCRYPTION_KEY="$(elixir -e ':crypto.strong_rand_bytes(32) |> Base.encode64() |> IO.puts()')"
fly secrets set DATABASE_URL="postgres://..."
fly deploy

API

The full OpenAPI 3.1 spec is at openapi/openapi.yaml. Public endpoints:

  • POST /api/v1/verifications — verify a signed message bundle
  • GET /api/v1/profiles/:username — public profile with attestations
  • POST /api/v1/messages — submit a signed message for hosted verification
  • GET /api/v1/messages/:id/verification — hosted verification result

Architecture

Browser / CLI / SDK
       │
       ▼  HTTPS
┌──────────────┐
│   Hallpass   │  Phoenix + Bandit
│   (Elixir)   │  React frontend (Vite)
└──────┬───────┘
       ▼  SSL
┌──────────────┐
│  PostgreSQL  │  Neon / self-hosted
└──────────────┘
  • Key modes: service-managed (server holds encrypted private key) or self-managed (server holds only public key)
  • Encryption: service-managed private keys are AES-256-GCM encrypted at rest via cloak_ecto
  • Auth: password (Argon2id), passkeys (WebAuthn/FIDO2), key-based challenge-response
  • Attestations: domain DNS TXT, X/Twitter post, GitHub gist, Bluesky handle resolution
  • Rate limiting: per-IP on auth endpoints via Hammer

Security

See SECURITY.md for the threat model, key custodianship details, and vulnerability reporting.

License

Apache 2.0 — see LICENSE.

About

Delegated agent identity and message verification for AI agents

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors