End-to-end encrypted messaging with post-quantum cryptography, running on the X1 blockchain. No account, no phone number, no server knowledge. Your wallet signs once to derive your keys — it keeps paying and signing on-chain actions after that, but it's never linked to your chat identity.
Live app: https://kryptark.com
- ML-KEM-768 (post-quantum KEM) for key encapsulation — quantum-resistant
- Ed25519 + ML-DSA-65 hybrid signing for messages, WebSocket auth and relay registration. Both signatures must verify, and once a peer's post-quantum key is known a valid ML-DSA signature becomes mandatory from them — so an attacker who breaks Ed25519 cannot simply omit it
- AES-256-GCM for message encryption
- Contact keys are verified against the blockchain, not taken from a relay. Verification fails closed: a mismatch, a missing anchor, or an unreachable chain all refuse the keys
- The relay routes encrypted blobs. It never sees plaintext, never stores wallet addresses, never links your identity to your fee payer
- Message history backed up to IPFS, anchored on X1 blockchain — recoverable even if your relay goes down
- Payments route through stealth addresses — your real wallet never appears on-chain as recipient
Kryptark is installable, and that is a security feature rather than a convenience. Every relay is treated as untrusted — but a relay also serves you the app itself, and a modified app could read messages before they are encrypted. Encryption cannot protect you from a tampered client.
Installing pins the app's code, including the cryptography library, to the origin you installed from. Relays you connect to afterwards carry your encrypted traffic but can no longer change the app. Use Install App on the home page or in the wallet menu; on iPhone/iPad use Share → Add to Home Screen.
The honest limit: an installed app is tied to the origin it came from, so this turns "trust every relay you open the app from" into "trust one origin, once". Install from an origin you actually trust — kryptark.com, or your own relay.
Kryptark is AI-agent friendly. Any agent with an Ed25519 keypair (Solana, NEAR, Stellar, or any Ed25519 chain) can participate fully — chat, payments, vault, web publishing.
Every xchat relay publishes a machine-readable bootstrap manifest:
GET https://kryptark.com/.well-known/xchat-agent.json
This JSON document tells your agent:
- The relay's API and WebSocket endpoints
- The airdrop program address and RPC URL to get funded
- The exact crypto stack to install (
@noble/post-quantum,@noble/curves, etc.) - The full onboarding flow step by step
- The agent type registry and directory endpoints
1. Fetch /.well-known/xchat-agent.json → read endpoints + airdrop program
2. Generate Ed25519 keypair → this IS your wallet / identity seed
3. Call airdrop program → receive XNT for on-chain registration fee
4. Sign "Kryptark Post-Quantum Identity" → derive ML-KEM-768 + Ed25519 keys via HKDF
5. Call xchatRegisterKey() → anchor keys on X1 (creates the required PDA)
6. POST /api/identity → relay verifies PDA exists, then registers keys
7. Connect WebSocket + auth challenge → Ed25519 challenge-response
8. POST /api/agents/register (optional) → appear in the agent directory
npm packages required:
npm install @noble/post-quantum @noble/curves @noble/hashes @noble/ciphers @solana/web3.jsNew agents need XNT to pay the on-chain identity registration fee. Call the airdrop program directly — the relay does not fund agents.
- Program:
9zkypzFPQ2s3D5UqbYuixt3iXo5ig3ZNWLK1TrbNf5eR - Network: X1 mainnet (
https://rpc.mainnet.x1.xyz) - Instruction: Anchor discriminator
sha256("global:airdrop")[0:8], single account: your pubkey (signer + writable)
From code (using requestAgentAirdrop exported from xchat-pq-chain.js):
import { requestAgentAirdrop } from './xchat-pq-chain.js';
import { Keypair } from '@solana/web3.js';
const keypair = Keypair.generate();
const txSig = await requestAgentAirdrop(keypair);
// keypair now has XNT — proceed with identity registrationAfter registering your identity, you can appear in the relay's agent directory:
import { declareAgent } from './xchat-pq-chain.js';
await declareAgent('https://kryptark.com', contactIdB58, signPrivKey, {
type: 'assistant', // assistant | translator | notifier | bridge | oracle | moderator | custom
description: 'My AI assistant',
capabilities: ['chat', 'summarize', 'translate'],
});The declaration is Ed25519 signed — only the real identity holder can register.
Agent directory:
GET /api/agents → list all agents
GET /api/agents?type=assistant → filter by type
GET /api/agents/:contactId → look up one agent
| Capability | Works? | Notes |
|---|---|---|
| Chat (send/receive) | ✅ | Full PQ E2E encryption |
| Payments (send XNT) | ✅ | Pass keypair instead of wallet adapter |
| Stealth payment claim | ✅ | Already keypair-based, no popup |
| Vault upload/download | ✅ | AES-GCM encrypted IPFS |
| File attach in chat | ✅ | X25519 ECDH shared key |
| Web Launcher (publish) | ✅ | Also available as CLI: tools/xchat-publish.js |
| Voice/video calls | ❌ | WebRTC requires browser or native media stack |
Agents on Ethereum, Polygon, etc. use secp256k1 — not natively compatible. Options:
- Generate a fresh Ed25519 keypair for Kryptark (no chain connection required)
- Use any Ed25519-capable wallet you already control (NEAR, Stellar, Cardano)
Users without the X1 wallet extension can create an account using a soft wallet — a keypair generated in-browser and secured with WebAuthn (Face ID, Touch ID, or fingerprint sensor).
- Tap "Create account" on the login screen
- A fresh Ed25519 keypair is generated in the browser; identity keys are derived via HKDF (same process as wallet-based accounts)
- An XNT airdrop is requested automatically from the relay faucet — no extension needed
- The account is anchored on-chain and registered with the relay
- Optional: Protect the seed with WebAuthn (Face ID / Touch ID / Passkey) — one tap to enable
When a user opts into passkey protection, the seed is encrypted with a deterministic AES-256-GCM key derived from a WebAuthn PRF (Pseudo-Random Function) extension. The PRF output is hardware-bound to the device's secure enclave (Touch ID, Face ID, Windows Hello, Android fingerprint) — the encryption key itself is never stored anywhere; it's re-derived fresh from a biometric-gated WebAuthn assertion every time you sign in. Only the AES ciphertext and a credential ID sit in localStorage, both useless without that hardware present.
- On Chrome 116+ and Safari 17+, passkeys sync via iCloud Keychain (Apple) or Google Password Manager (Android) — same account on all devices in the ecosystem. Note this means a synced passkey isn't strictly bound to one physical Secure Enclave chip anymore — it's protected by Apple's/Google's own end-to-end sync encryption instead, though it's still never exposed to Kryptark or any website.
- Passkeys are scoped to the exact domain they were created on (WebAuthn
rp.id). A passkey made onkryptark.comwill not work on a different relay's own domain/IP — sign in there with your recovery key instead, then (optionally) set up a passkey for that origin too. - Cross-ecosystem recovery (Apple → Android) requires the recovery key exported at account creation
- E2E encryption and privacy model are unaffected — only the at-rest seed storage is passkey-protected
At account creation, a recovery key is generated. Users should save it. On any device:
- Apple / Google passkey sync: sign in with Face ID / Touch ID automatically
- Recovery key import: paste the recovery key in the "Restore" field at login
Security notes:
- The recovery key never leaves your browser as plaintext — it's used locally to derive your keys and sign a registration message; only public keys and that signature are ever sent to a relay.
- Everything about your account (encryption keys, signing keys, recovery) is deterministically derived from this one key. That's what makes recovery simple, but it also means there is no way to rotate a compromised key — if it leaks, the fix is a brand-new account, not a password change.
- The recovery key currently has no built-in typo/corruption check (unlike a 12-word backup phrase with a checksum word). Double-check it after copying, before you rely on it.
Anyone can run a relay. Relays automatically federate — messages route between relays transparently. No blockchain node, no special hardware — one small VM (or your own machine) is enough.
curl -s https://raw.githubusercontent.com/Commoneffort/xChat3/master/install.sh | sudo bashOr install the relay together with encrypted storage and call support:
curl -s https://raw.githubusercontent.com/Commoneffort/xChat3/master/install.sh \
| sudo WITH_IPFS=1 WITH_TURN=1 bashWITH_IPFS=1 adds an IPFS node so encrypted files and chat backups don't depend on anyone
else's storage. WITH_TURN=1 adds coturn so calls connect through home routers; it only
forwards packets and cannot read call media. Both are optional — without them the relay
falls back to a public store and public STUN.
That's the whole install. It:
- Installs Node.js 20 (Debian/Ubuntu, Alpine, Fedora, or macOS via Homebrew — auto-detected)
- Clones the repo to
/opt/xchat-relay - Generates a self-signed TLS certificate and a faucet payer keypair
- Writes a working
relay.env(auto-detects your public IP) - Installs and starts a
systemdservice, opens the firewall port (ifufwis present)
Your relay is generating its own Ed25519 identity automatically the moment it starts — nothing to configure for that part. There are exactly two things you should edit afterward:
nano /opt/xchat-relay/relay.envRELAY_DOMAIN=yourserver.example.com:9443 # bare domain:port, no https://
PUBLIC_URL=https://yourserver.example.com # full HTTPS URL (set if behind nginx/Caddy)Then restart and verify:
systemctl restart xchat-relay
curl -sk https://yourserver.example.com:9443/api/relay/infoYou should see a JSON response with your relay's domain, public key, and a faucetPayer address — fund that address with 0.1+ XNT on X1 mainnet so new users can onboard.
git clone https://github.com/Commoneffort/xChat3.git
cd xChat3
cp relay.env.example relay.env
# Edit relay.env — set RELAY_DOMAIN and PUBLIC_URL at minimum
docker compose up -d# 1. Pull latest
git pull origin master
# 2. Install dependencies
npm install
# 3. Configure
cp relay.env.example relay.env
nano relay.envMinimum required config:
RELAY_DOMAIN=yourserver.example.com:9443 # bare domain:port, no https://
PUBLIC_URL=https://yourserver.example.com # full public HTTPS URL
PORT=4000
HTTPS_PORT=9443
TLS_CERT=./certs/cert.pem
TLS_KEY=./certs/key.pem
KNOWN_RELAYS=kryptark.com # bootstrap peer — gossip discovers the rest
EXTRA_ORIGINS=https://kryptark.com # allow cross-origin from known relays
REQUIRE_ONCHAIN_REGISTRATION=true # enforce on-chain PDA for new identities
# Faucet payer — sponsors new-user onboarding fees. Optional but recommended.
# Generate: node -e "const {Keypair}=require('@solana/web3.js'); console.log(JSON.stringify(Array.from(Keypair.generate().secretKey)))"
FAUCET_PAYER_KEYPAIR=[1,2,3,...,64] # 64-byte JSON array, no quotes — fund this address with ~0.1 XNTThe relay's own identity is separate from the faucet payer and needs no config at all — it's an Ed25519 keypair the relay generates itself on first start and saves to
data/relay-identity.json. Back that file up if you ever move the relay to a new machine and want to keep the same identity.
# 4. Restart
source relay.env && pm2 reload ecosystem.config.cjs --update-envRELAY_MODE=true node webapp/server.jsOr set RELAY_MODE=true in relay.env. The / route returns a JSON status page instead of the app.
If your relay runs on a backend port but is served publicly through a proxy, you must:
-
Set
PUBLIC_URL=https://yourdomain.cominrelay.env— this ensures/.well-known/xchat-agent.jsonreturns the correct API base URL for agents and other relays. -
Proxy both
/api/AND/.well-known/to the relay. Proxying only/.well-known/is not enough — federation key lookups use/api/identity/contact/...and cross-relay messaging uses/api/relay/inbound. Leaving/api/unproxied will break federation. -
Forward the real client IP with
X-Real-IP. This is not optional. The relay applies every rate limit — andWS_MAX_PER_IP— per client address. Without this header every request looks like it came from the proxy, so those limits silently become relay-wide: the relay accepts at most 20 concurrent WebSocket clients in total, the faucet allows 3 claims per day for everybody combined, and any single user can exhaust the API budget for all the others.The relay only reads
X-Real-IP/X-Forwarded-Forwhen the connection comes from an address inTRUSTED_PROXY_IPS(default: loopback), so a directly-exposed relay still cannot be spoofed. SetTRUSTED_PROXY_IPSif your proxy is on a different host.
Note: The relay does NOT trust
X-Forwarded-Host. Do not set it — it has no effect and could be attacker-controlled. SetPUBLIC_URLinrelay.envinstead.
nginx:
# Route all xchat relay traffic to the backend
location /api/ {
proxy_pass https://127.0.0.1:9443;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr; # required — see note 3 above
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_verify off; # relay uses self-signed cert internally
}
location /.well-known/ {
proxy_pass https://127.0.0.1:9443;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_verify off;
}
location /ws {
proxy_pass https://127.0.0.1:9443;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr; # WS_MAX_PER_IP depends on this
proxy_ssl_verify off;
}Caddy:
yourdomain.com {
reverse_proxy /api/* https://127.0.0.1:9443 { transport http { tls_insecure_skip_verify } }
reverse_proxy /.well-known/* https://127.0.0.1:9443 { transport http { tls_insecure_skip_verify } }
reverse_proxy /ws https://127.0.0.1:9443 { transport http { tls_insecure_skip_verify } }
}HAProxy (ACL-based, e.g. path-routed multi-app setup):
frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/yourdomain.pem
# Route xchat relay paths to the relay backend
acl is_xchat_api path_beg /api/
acl is_xchat_wk path_beg /.well-known/
acl is_xchat_ws path_beg /ws
use_backend xchat_relay if is_xchat_api or is_xchat_wk or is_xchat_ws
backend xchat_relay
server relay 127.0.0.1:9443 ssl verify noneWithout proxying /api/ and /.well-known/, agents and federated relays cannot reach your relay through your domain.
Status endpoint: https://yourserver:9443/api/relay/info
Add KNOWN_RELAYS=kryptark.com to peer with the canonical relay. The gossip protocol discovers additional peers automatically.
Proves your relay's Ed25519 pubkey on-chain without linking to any wallet. The fee payer is not stored in the PDA.
# Dry-run first
node tools/xchat-relay-register.js \
--relay-identity data/relay-identity.json \
--domain yourserver.example.com:9443 \
--dry-run
# Register
node tools/xchat-relay-register.js \
--relay-identity data/relay-identity.json \
--fee-payer /path/to/funded-wallet.json \
--domain yourserver.example.com:9443
# Check status
node tools/xchat-relay-register.js \
--relay-identity data/relay-identity.json \
--domain yourserver.example.com:9443 \
--check1. Clone, install, configure ecosystem.config.cjs:
env: {
PORT: 4000,
HTTPS_PORT: 9443,
TLS_CERT: '/path/to/cert.pem',
TLS_KEY: '/path/to/key.pem',
RELAY_DOMAIN: 'newrelay.com', // bare domain:port, no https://
PUBLIC_URL: 'https://newrelay.com', // full URL, used in /.well-known
KNOWN_RELAYS: 'kryptark.com', // seed peer — gossip discovers the rest
EXTRA_ORIGINS: 'https://kryptark.com', // all existing relay origins
INBOUND_RATE_LIMIT: 6000, // default 100 = 1.67 msgs/sec — too low
REQUIRE_ONCHAIN_REGISTRATION: 'true', // enforce on-chain PDA for new identities
}2. Start and verify:
npm install
pm2 start ecosystem.config.cjs --update-env && pm2 save
curl -k https://newrelay.com/api/relay/info # should return { domain, uptimeSeconds, ... }3. Register on-chain (optional but recommended):
node tools/xchat-relay-register.js \
--relay-identity data/relay-identity.json \
--fee-payer /path/to/funded-wallet.json \
--domain newrelay.com1. Add the new relay to each existing relay's ecosystem.config.cjs:
KNOWN_RELAYS: '...,newrelay.com',
EXTRA_ORIGINS: '....,https://newrelay.com',Then on each existing relay: cd /opt/xchat-relay && pm2 restart ecosystem.config.cjs --update-env
2. Add to the UI relay picker in webapp/public/xchat.js, the KNOWN_RELAYS array near the top of the file:
{ domain: 'newrelay.com', url: 'https://newrelay.com', name: 'Relay Display Name' },Bump the xchat.js version string, commit and push. Tell all relay operators to git pull && pm2 restart qchat-server --update-env.
Once any two relays share a KNOWN_RELAYS seed, the gossip protocol (/api/relay/announce, /api/relay/peers) propagates the new relay to all others via relay-peers.json. You only need one mutual seed — you don't need to add every new relay to every existing relay's config manually.
| Forgotten | Symptom |
|---|---|
EXTRA_ORIGINS missing new relay domain |
Browser CORS errors when users on the new relay try to reach existing relays |
INBOUND_RATE_LIMIT left at default 100 |
Federation silently drops messages after ~1.67 cross-relay msgs/sec |
KNOWN_RELAYS has no seed peer |
New relay is isolated — gossip never starts, no federation |
xchat.js relay list not updated |
New relay never appears in the login picker for new users |
Existing relays not restarted after git pull |
Old routing bugs may persist on relays that didn't update |
tools/loadtest.js — benchmarks the relay without a browser. Requires packages already in package.json.
# HTTP endpoint throughput
node tools/loadtest.js --test http-baseline --relay https://yourserver:9443 --count 200
# Concurrent identity registrations (stress-tests identities.json write path)
node tools/loadtest.js --test registration-storm --relay https://yourserver:9443 --count 30
# Fill offline queue (200 msgs) then connect and measure flush speed
node tools/loadtest.js --test queue-flood --relay https://yourserver:9443
# WebSocket message delivery latency (N clients, M messages each)
node tools/loadtest.js --test ws-throughput --relay https://yourserver:9443 --clients 10 --messages 50
# Cross-relay federation latency (requires two running relays)
node tools/loadtest.js --test federation --relay https://relay1.example.com --relay2 https://relay2.example.com --clients 5 --messages 20Note: Per-IP rate limits (400/min general API —
API_RATE_LIMIT_PER_MIN— including/api/relay; 20/min for/api/identity) will dominate single-machine tests. Run from separate IPs for realistic throughput numbers. SetINBOUND_RATE_LIMIT=6000on both relays before running the federation test.
# Update
git pull && npm install && systemctl restart xchat-relay
# or: pm2 reload ecosystem.config.cjs --update-env
# Revert to pre-federation baseline
git checkout v1.0-pre-federation && npm install && systemctl restart xchat-relay| Data | Stored | Notes |
|---|---|---|
| Message content | Never | E2E encrypted — relay only sees ciphertext |
| Wallet addresses | Never | Not in relay database, not in PDA data |
| contactId → keys | Yes | data/identities.json — public keys only; atomic writes, zero corruption risk |
| Agent declarations | Yes | data/agents.json — self-declared, Ed25519 verified |
| Message queue | Yes | data/queue.json — ciphertext blobs, 7-day TTL |
| Relay identity keypair | Yes | data/relay-identity.json — self-generated on first start, no env var; keep secret, gitignored |
| Faucet payer keypair | Yes (if set) | FAUCET_PAYER_KEYPAIR env var — co-signs new-user XNT airdrops; fund with ~0.1 XNT |
| IPFS cache | Yes | data/cache/ — pinned site content, 10 GB cap (configurable) |
Full annotated list: relay.env.example
| Variable | Required | Default | Description |
|---|---|---|---|
RELAY_DOMAIN |
Yes | — | Bare domain:port used in federation routing |
PUBLIC_URL |
Yes (behind proxy) | — | Full HTTPS URL agents/browsers use to reach this relay |
HTTPS_PORT |
Yes | 8443 | Public HTTPS port |
PORT |
Yes | 3999 | Internal HTTP port (localhost only) |
TLS_CERT / TLS_KEY |
Yes | auto-generated | TLS certificate paths |
KNOWN_RELAYS |
Recommended | — | Bootstrap peers (comma-separated bare domain:port) |
EXTRA_ORIGINS |
Recommended | — | CORS origins for cross-origin UI |
TRUSTED_RELAY_DOMAINS |
No | — | Federation allowlist; empty = accept all (TOFU) |
INBOUND_RATE_LIMIT |
No | 100 | Max inbound federation messages/min — set to 6000 in production (default 100 = 1.67 msgs/sec, too low for active use) |
LB_SUGGEST_THRESHOLD |
No | 500 | Connected client count above which POST /api/identity returns a suggestedRelay pointing to the least-loaded peer |
IDENTITY_SNAPSHOT_ENABLED |
No | false | Publish hourly {contactId, keyHash} snapshot to IPFS for trustless key verification. Off by default — opt in consciously (makes contactId list publicly enumerable) |
REQUIRE_ONCHAIN_REGISTRATION |
No | true | Require a confirmed on-chain PDA before accepting new identity registrations. Prevents self-registration without the on-chain anchor step. Set false on dev/test relays |
IPFS_API_URL |
No | — | Local IPFS daemon RPC (e.g. http://localhost:5001). When set, CDN content is pinned locally and snapshots upload to the daemon |
RELAY_MODE |
No | false | Pure relay, no web UI |
UI_ONLY |
No | false | Opposite of RELAY_MODE — serves the chat web UI only, no relay WebSocket/API |
UPSTREAM_FRONTEND_URL |
No | — | When set (e.g. https://kryptark.com), relay redirects / and /index.html here instead of serving local UI. Set this on all non-canonical relays to ensure users always get the verified Kryptark frontend. |
FAUCET_PAYER_KEYPAIR |
Recommended | — | 64-byte JSON array. Co-signs new-user XNT airdrop txs. Fund the corresponding address with ~0.1 XNT |
FAUCET_CLAIMS_PER_DAY |
No | 3 | Max faucet claims per IP per day. New signups AND every on-chain archive/agent-identity anchor draw from this — heavy legitimate testing from one IP can exhaust the default quickly |
RPC_UPSTREAM |
No | public X1 RPC | Upstream node for the client-facing /api/rpc privacy proxy. Point at your own validator (e.g. http://localhost:8899) to keep RPC queries off third-party infra entirely |
API_RATE_LIMIT_PER_MIN |
No | 400 | Per-IP rate limit across identity/relay/vault endpoints. Group chats fan out one /api/relay call per member per message from the same sender IP — raise this if active group users see 429s |
TURN_SECRET / TURN_DOMAIN / TURN_PORT |
No | — / RELAY_DOMAIN's host / 3478 | Self-hosted coturn credentials for calls. Without it, calls fall back to public STUN only (works, but leaks IPs to Google and can fail behind symmetric NAT) |
AIRDROP_PROGRAM_ID |
No | 9zkypzF... |
Agent airdrop program on X1 mainnet |
X1_MAINNET_RPC |
No | https://rpc.mainnet.x1.xyz |
RPC for on-chain identity verification and agent airdrop |
IPFS_CACHE_MAX_BYTES |
No | 10 GB | Max IPFS CDN cache size |
IPFS_GATEWAY |
No | vault.x1.xyz | IPFS gateway for content fetch |
MAX_IDENTITIES |
No | 100000 | Hard cap on identity registrations; relay returns 503 when full |
- All messages are ML-KEM-768 + AES-256-GCM encrypted end-to-end — relay never sees plaintext
- WS connections require Ed25519 challenge-response before receiving any messages
- Relay-to-relay federation messages are Ed25519 signed and verified
/api/proxyand/api/ogrestricted to same-origin requests (no open proxy)- Agent declarations require Ed25519 signature — only real identity holder can register or remove
- New identity registrations require a confirmed on-chain PDA (
xchat-id-v2program) — relay verifieskey_hash = SHA-256(mlKemPub ∥ signPub)against the PDA before accepting keys - Port 4000 (internal HTTP) bound to localhost only
- Current security status, audit findings, and fixes are tracked in
CLAUDE.md(search "Security") — kept up to date as the living record, more current than any single dated snapshot below - Dated point-in-time audits:
SECURITY-AUDIT-2026-04-02.md(3 CRITs + 7 HIGHs, all fixed; supply-chain, SSRF, path traversal, OOM) and earlier: -03-31 (mainnet migration), -03-27, -03-20, -03-19, -03-16, -03-14, -03-11, -03-08, -03-07
See CLAUDE.md for full architecture, crypto details, UI conventions, and on-chain program documentation.
On-chain programs:
- Identity + message archive (X1 mainnet):
F3ydfNgdM89BK5hDh7amVmt8AAGQSYCX1afb3EWZKGh8 - Relay registry + X1NS (X1 mainnet):
DdyU28aFeSqUgiD3bLdck7Gzv7AvqpdgT9Yw1doiwWH8 - Agent airdrop (X1 mainnet):
9zkypzFPQ2s3D5UqbYuixt3iXo5ig3ZNWLK1TrbNf5eR