This demonstrates freeq's cryptographic channel governance: policy-gated channels with auditable membership, built on standard IRC.
cargo build --release --bin freeq-server)curl and python3 (for API inspection)# Terminal 1: Start server
cargo run --release --bin freeq-server -- \
--listen-addr 127.0.0.1:16799 \
--web-addr 127.0.0.1:8080
# Terminal 2: Run interactive demo
./scripts/demo.sh
The demo script walks you through each scene with pauses. Or follow the manual steps below.
Any IRC client works. Using netcat to prove the point:
$ nc 127.0.0.1 16799
NICK guest42
USER guest 0 * :Guest
JOIN #general
PRIVMSG #general :Hello from a standard IRC client!
This works. Always will. Policy framework doesn't break existing IRC.
Open the web client at http://127.0.0.1:8080:
Or use the TUI client with SASL.
As the authenticated user (who'll be channel op):
/join #freeq-dev
You're the founder — you have ops. Now set a policy:
/POLICY #freeq-dev SET By joining this channel you agree to the freeq Code of Conduct: be respectful, no spam, no harassment. Violations result in removal.
Response:
Policy set for #freeq-dev (version 1, rules_hash=a1b2c3d4e5f6, policy_id=d4e5f6789abc)
You're auto-attested (the op who created the policy is always accepted).
The guest user tries:
JOIN #freeq-dev
Response:
477 guest42 #freeq-dev :This channel requires authentication — sign in to join
Clean rejection. The guest can still use #general and any other open channel.
A second user signs in with Bluesky, then:
/POLICY #freeq-dev INFO
Response:
Policy for #freeq-dev:
Version: 1
Policy ID: d4e5f6789abc...
Effective: 2026-02-18T15:55:32+00:00
Validity: JoinTime
Requirement: ACCEPT(a1b2c3d4e5f6...)
Accept the policy:
/POLICY #freeq-dev ACCEPT
Response:
Policy accepted for #freeq-dev — role: member. You may now JOIN.
Now join:
/join #freeq-dev
Success. They're in with a verified badge.
Current policy:
curl -s http://127.0.0.1:8080/api/v1/policy/%23freeq-dev | python3 -m json.tool
{
"policy": {
"channel_id": "#freeq-dev",
"policy_id": "d4e5f6...",
"version": 1,
"effective_at": "2026-02-18T15:55:32.123456+00:00",
"authority_set_hash": "abc123...",
"requirements": {
"type": "ACCEPT",
"hash": "a1b2c3..."
},
"validity_model": "join_time",
"receipt_embedding": "require"
},
"authority_set": {
"channel_id": "#freeq-dev",
"signers": [{"did": "did:web:127.0.0.1", ...}],
"policy_threshold": 1
}
}
Transparency log (privacy-preserving — no DIDs):
curl -s http://127.0.0.1:8080/api/v1/policy/%23freeq-dev/transparency | python3 -m json.tool
[
{
"entry_version": 1,
"channel_id": "#freeq-dev",
"policy_id": "d4e5f6...",
"attestation_hash": "789abc...",
"issued_at": "2026-02-18T15:56:01.000000+00:00",
"issuer_authority_id": "did:web:127.0.0.1"
}
]
Check specific membership:
curl -s http://127.0.0.1:8080/api/v1/policy/%23freeq-dev/membership/did:plc:abc123 | python3 -m json.tool
Policy version history:
curl -s http://127.0.0.1:8080/api/v1/policy/%23freeq-dev/history | python3 -m json.tool
/POLICY #freeq-dev SET Updated Code of Conduct v2: be respectful, no spam, no harassment, no AI-generated content without disclosure.
This creates version 2, chained to version 1. Existing members keep their attestations (JoinTime model). New joiners must accept v2.
/POLICY #freeq-dev CLEAR
Channel returns to open join. All attestations and logs are removed.
POLICY #myproject SET <Code of Conduct text>
This is a full working example. A project channel where accepting the Code of Conduct gets you in, but GitHub org members automatically get ops.
Step 1: Create the channel and set base policy
/join #myproject
/POLICY #myproject SET By contributing to this project you agree to our Code of Conduct: be respectful, inclusive, and constructive.
Step 2: Add role escalation — org members get ops
First, get the rules hash from POLICY INFO:
/POLICY #myproject INFO
→ Requirement: ACCEPT(a1b2c3d4e5f6...)
Then set the "op" role requirement (replace the hash):
/POLICY #myproject SET-ROLE op {"type":"ALL","requirements":[{"type":"ACCEPT","hash":"a1b2c3d4e5f6...full-hash..."},{"type":"PRESENT","credential_type":"github_membership","issuer":"github"}]}
Step 3: A contributor verifies their GitHub identity
With GitHub OAuth configured (GITHUB_CLIENT_ID + GITHUB_CLIENT_SECRET):
/POLICY #myproject VERIFY github myorg
→ Open this URL to verify your GitHub identity: http://127.0.0.1:8080/auth/github?did=...&org=myorg
Click the link → GitHub OAuth → authenticate → server confirms org membership → credential stored. The server knows your real GitHub username from the OAuth token — no self-attestation possible.
Without GitHub OAuth (public API fallback — weaker):
/POLICY #myproject VERIFY github myorg octocat
→ ⚠ Note: public API check cannot prove you own this GitHub account
→ ✓ octocat is a public member of myorg. Credential stored (⚠ not OAuth-verified).
Step 4: The contributor accepts the policy
/POLICY #myproject ACCEPT
→ Policy accepted for #myproject — role: op. You may now JOIN.
They got op because they have both the CoC acceptance AND the GitHub credential.
Step 5: Join — auto +o
/join #myproject
→ (joined with +o automatically)
A user without GitHub membership who accepts the same policy gets member role — no ops, but they can chat.
API alternative (for web/mobile):
# Verify GitHub membership
curl -X POST http://localhost:8080/api/v1/verify/github \
-H 'Content-Type: application/json' \
-d '{"did":"did:plc:abc123","github_username":"octocat","org":"myorg"}'
# Check stored credentials
curl http://localhost:8080/api/v1/credentials/did:plc:abc123
# Submit join with evidence
curl -X POST http://localhost:8080/api/v1/policy/%23myproject/join \
-H 'Content-Type: application/json' \
-d '{"subject_did":"did:plc:abc123","accepted_hashes":["a1b2c3..."],"credentials":[{"credential_type":"github_membership","issuer":"github"}]}'
member roleop role → automatic +o on joinPOLICY #private SET PRESENT(invitation, issuer=did:plc:channelowner)
POLICY #holders SET PROVE(nft_ownership)
POLICY #acme-eng SET ALL(ACCEPT(employee_handbook), PRESENT(email_verified, issuer=acme.com))
| Action | IRC Layer | Policy Layer | Crypto Layer |
|---|---|---|---|
/POLICY SET |
NOTICE reply | PolicyDocument created, versioned | SHA-256 hash chain |
/POLICY ACCEPT |
NOTICE reply | Evidence evaluated, JoinReceipt + MembershipAttestation created | HMAC-SHA256 signed attestation |
JOIN #channel |
Standard JOIN flow | Attestation checked (or skipped if no policy) | Signature verified |
| Policy update | NOTICE reply | New version chained to previous | Hash chain integrity |
| S2S sync | — | PolicySync message to peers | Attestation signatures portable |
ACCEPT { hash } — User accepted rules document
PRESENT { type, issuer } — User has credential of type from issuer
PROVE { proof_type } — User can prove capability
ALL { requirements[] } — All must be satisfied
ANY { requirements[] } — At least one must be satisfied
NOT { requirement } — Must NOT be satisfied
Max depth: 8. Max nodes: 64. Deterministic, fail-closed.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/policy/{channel} |
Current policy + authority set |
| GET | /api/v1/policy/{channel}/history |
Full policy version chain |
| POST | /api/v1/policy/{channel}/join |
Submit evidence, receive attestation |
| GET | /api/v1/policy/{channel}/membership/{did} |
Check membership status |
| GET | /api/v1/policy/{channel}/transparency |
Audit log entries |
| GET | /api/v1/authority/{hash} |
Authority set by hash |
{
"subject_did": "did:plc:abc123",
"accepted_hashes": ["a1b2c3..."],
"credentials": [
{"credential_type": "github_membership", "issuer": "github"}
],
"proofs": ["nft_ownership_proof"]
}
Response (success):
{
"status": "confirmed",
"join_id": "deadbeef...",
"attestation": {
"attestation_id": "...",
"channel_id": "#freeq-dev",
"subject_did": "did:plc:abc123",
"role": "member",
"issued_at": "2026-02-18T15:56:01Z",
"signature": "hmac-sha256:..."
}
}
Response (rejected):
{
"status": "failed",
"error": "Requirement not satisfied: ACCEPT(a1b2c3...)"
}