# HexaEight — Full API Reference (AI-readable mirror) > This is the flat-text mirror of https://hexaeight.com/docs intended for AI agents, > retrieval pipelines, and any tool that prefers a single plain-text file over HTML. > For the rendered version with navigation, visit /docs. Last updated: 2026-06-07. Reference Bridge version: 1.0.0-preview11. SUPPORTED CONVERSATION LIFECYCLE: Phase 1 — JWT handshake (one-shot). Alice's first message to Bob is a HexaEight-encrypted JWT carrying the body and a session ID. The JWT's plaintext header includes `issuer` (Alice's Name) and `kgt` — Bob reads both with zero crypto. Bob fetches his half of the ASK pair for Alice and validates the JWT; validation produces the verified claims, including `iss` (cryptographically guaranteed sender), the session ID, and the body. Phase 2 — Sessioned envelopes (steady state). Both sides pin their respective halves of the ASK pair under the shared session ID. Every subsequent message in either direction is a Variant B envelope `hsha:sha256(sessionId)|ciphertext` — no identity on the wire, no per-message ASK fetch, sub-200ms encrypt/decrypt. ANONYMOUS-SENDER MODE (Variant A — Standard envelopes with sender prefix on the wire) is in the API surface but not yet recommended for production round-trips. Coming soon. --- ## TABLE OF CONTENTS 1. Overview 2. Three-layer architecture 3. SDK status matrix 4. Identity setup 5. Quickstart (.NET + Node, sessioned round-trip) 6. Concept: Envelopes 7. Concept: ASK (Asymmetric Shared Keys) 8. Concept: KGT (Key Generation Time) 9. Wire format specification 10. Bridge API reference — every method 11. Bridge API reference — records and enums 12. Error semantics 13. Performance characteristics 14. Cross-language SDK naming map 15. Guides — agent-to-agent, sessioned, cache-persist 16. License 17. Contact --- ## 1. OVERVIEW HexaEight is a quantum-resistant, password-based asymmetric encryption system. Each identity has a different password (never shared). The platform mediates key derivation but is mathematically unable to decrypt any message — only password holders can. Patent filed July 2021. The HexaEight.Bridge .NET package is the canonical SDK. All other language SDKs (Node, Python, Browser) wrap it through their language's CoreCLR hosting layer (node-api-dotnet, pythonnet, etc.) so the API surface is identical across languages. --- ## 2. THREE-LAYER ARCHITECTURE Layer 0 — Crypto. HexaEight.Bridge (.NET). Identity, ASK derivation, envelope encrypt/decrypt. Layer 1 — Transport. Per-language SDK. HTTP, ntfy, webhooks, MQ — how the envelope reaches its destination. Layer 2 — Application. Your code. Storage policy, sender abstractions, deny-lists, app logic. The cryptographic core is identical in every language. Only Layer 1 and Layer 2 are language-specific. --- ## 3. SDK STATUS MATRIX SDK Package Status ---------- --------------------------- -------------------------------- .NET HexaEight.Bridge (NuGet) preview12 — shipping (Windows/Linux/macOS) Node.js @hexaeight/sdk (npm) preview3 — shipping (Linux/macOS/WSL2; not native Windows, see microsoft/node-api-dotnet#479) Python hexaeight-sdk (PyPI) in progress Browser WASM via Bridge planned --- ## 3.5 IDENTITY TYPES AND COMMUNICATION RULES (added 2026-06-09) HexaEight has two kinds of identity: USER IDENTITY Form: Email address (e.g. alice@example.com) Issuance: HexaEight Authenticator mobile app — user signs in with their email and the app generates a per-device identity bound to it. Authentication: The Authenticator app is the ONLY authentication channel for a user identity. When the browser SDK ships, it will use the same Authenticator app for sign-in. There is no password-based or magic-link channel for users. GENERIC AGENT IDENTITY Form: Auto-generated handle web0-bliss-cyan-. First three words derived from the issuing user's email (so no other email can claim the same prefix). Final word rotates per issuance. Issuance: The Authenticator app generates the handle against the user's email and binds it to a HexaEight identity license. Authentication: Tied to the machine via `npx hexaeight-activate` (or equivalent) — the hexaeight.mac binding file authenticates the agent thereafter. CUSTOM-HOSTNAME AGENT IDENTITY Form: Hostname under a domain the user owns (agent01.acme.com). Issuance: Same activation flow as a generic agent, with an additional DNS TXT record proof of domain ownership required before the platform issues the identity. Authentication: Same machine binding as a generic agent. User identities can only be created on the user's phone (via the Authenticator app). Agent identities can be created on any machine by a user who has a license to spend. The point of agent identity is that the user delegates cryptographic agency to a hostname-bound process — agents can act autonomously without the user being online. COMMUNICATION RULES User -> Agent ALLOWED Direct. Agent -> User ALLOWED Direct. Agent -> Agent ALLOWED Direct. User -> User (direct) NOT ALLOWED — the platform will not issue a user-to-user ASK pair. User -> User (via a DESIGNATED-RELAY-AGENT) ALLOWED Indirect. The DESIGNATED-RELAY-AGENT pattern is the ONLY way one user can contact another. The sending user encrypts to an agent they explicitly trust — that agent relays to the destination user (privately re-encrypting, or as an agentic mediator that summarises / filters / queues the message). This is a deliberate security property — it eliminates user-to-user spam and unsolicited-message attacks. --- ## 4. IDENTITY SETUP You receive these files when you buy a HexaEight identity license: env-file Plain text. Four variables: HEXAEIGHT_LICENSECODE= HEXAEIGHT_MACHINETOKEN= HEXAEIGHT_RESOURCENAME= HEXAEIGHT_SECRET= hexaeight.mac Binary. Machine-bound. Must be HARD-LINKED into your project directory (not copied — the kernel-level link is part of the binding check). hexaeightkeys.db (Azure Marketplace mode only) — SQLite. The Bridge reads settings.logintoken from this instead of the env-file when it detects this file in the CWD. Bridge identity load order (first hit wins): 1. hexaeightkeys.db in CWD (Azure mode) 2. HEXAEIGHT_MACHINETOKEN env variable (non-Azure runtime) 3. env-file in CWD with HEXAEIGHT_MACHINETOKEN=… line (non-Azure dev/CI) The Bridge is READ-ONLY with respect to your environment — it never sets or modifies environment variables on your behalf. --- ## 5. QUICKSTART — JWT HANDSHAKE + SESSIONED FOLLOW-UP Two HexaEight identities (Alice and Bob), each loaded in its own working directory. Phase 1 is the JWT handshake (one-shot). Phase 2 is sessioned messaging. ### .NET — Phase 1: JWT handshake (Alice initiates) dotnet add package HexaEight.Bridge --prerelease using HexaEight.Bridge; var alice = new Client(); string bob = "bob.example.com"; string sid = Guid.NewGuid().ToString(); long kgt = CurrentKgt(); string ask = await alice.FetchAskAsync(bob, kgt); alice.PinAskForSession(sid, ask); alice.ClearAllPayloadItems(); alice.AddPayloadItem("BODY", "Hello Bob - first message of our conversation"); alice.AddPayloadItem("SESSIONID", sid); string jwt = await alice.CreateEncryptedJwtTokenUsingSharedKeyAsync(bob, 5, kgt, ask); // Send `jwt` to Bob over any transport. ### .NET — Phase 1 (continued): Bob validates and pins using HexaEight.Bridge; using System.Text.Json; var bob = new Client(); string jwt = await ReceiveFromWire(); // Parse plaintext JWT header — no crypto required var hdr = ParseJwtHeader(jwt); string claimedSender = hdr.GetProperty("issuer").GetString(); long kgt = hdr.GetProperty("kgt").GetInt64(); // Fetch Bob's half of the ASK pair for that sender string ask = await bob.FetchAskAsync(claimedSender, kgt); // Validate JWT — returns claims JSON on success, "-..." on failure string claimsJson = await bob.ValidateTokenUsingSharedKeyAsync(jwt, ask); var c = JsonDocument.Parse(claimsJson).RootElement; string verifiedSender = c.GetProperty("iss").GetString(); // cryptographic string sid = c.GetProperty("SESSIONID").GetString(); string body = c.GetProperty("BODY").GetString(); // Pin Bob's ASK under the same session for steady-state messaging bob.PinAskForSession(sid, ask); ### .NET — Phase 2: Sessioned envelopes (either direction) // Bob replies string reply = await bob.EncryptEnvelopeAsync(verifiedSender, "pong", sessionId: sid); // Alice receives DecryptedEnvelope msg = await alice.DecryptEnvelopeAsync(reply); // msg.Sender == "bob.example.com" (cryptographically verified) // msg.Body == "pong" // msg.FromSession == true static long CurrentKgt() { long m = (long)(DateTime.UtcNow - DateTime.UnixEpoch).TotalMinutes; return m - (m % 15); } ### Node.js npm install @hexaeight/sdk@preview import { HexaEight } from '@hexaeight/sdk'; const alice = await HexaEight.connect(); const sessionId = crypto.randomUUID(); const kgt = currentKgt(); const ask = await alice.ask.fetch('bob.example.com', kgt); alice.ask.pinForSession(sessionId, ask); const env = await alice.envelope.encrypt('bob.example.com', 'Hello Bob!', { sessionId }); // On Bob's machine const bob = await HexaEight.connect(); const askB = await bob.ask.fetch('alice.example.com', kgt); bob.ask.pinForSession(sessionId, askB); const msg = await bob.envelope.decrypt(env); console.log(msg.sender, msg.body); // "alice.example.com" "Hello Bob!" --- ## 6. CONCEPT — ENVELOPES ### Variant B — Sessioned (SUPPORTED) hsha:{sha256(sessionId)}|{ciphertext} hsha: Literal 5-byte prefix (ASCII). sessionHash SHA-256 of session identifier, lowercase hex, exactly 64 chars. ciphertext MQ-V4 ciphertext, Base64URL. Sender identity is NOT on the wire. The recipient learns the sender via the cryptographically asserted Sender field inside the encrypted JSON, exposed as DecryptedEnvelope.Sender. ### Variant A — Standard (PREVIEW, not recommended) {sourceId}|{kgt}|{ciphertext} sourceId Sender's login-token prefix. Implementation-dependent length. kgt Unix minute floored to nearest 15. int64. ciphertext MQ-V4 ciphertext, Base64URL. Variant A is in the wire format but not yet recommended for production round-trips — sourceId depends on the sender's login-token format. Anonymous-sender mode (no inner sender claim, sender genuinely unknown to the recipient) is planned. For now, use Variant B. ### Variant detection if envelope.startsWith("hsha:"): parse as Variant B else: parse as Variant A ### Inspection without decryption Client.InspectEnvelope (static method) parses public fields only: var inspected = Client.InspectEnvelope(envelope); if (inspected.Kind == EnvelopeKind.Sessioned) { // inspected.SessionHash (64 hex chars) } else { // inspected.SourceId, inspected.Kgt } --- ## 7. CONCEPT — ASK (ASYMMETRIC SHARED KEYS) ASK is the cryptographic primitive. The platform issues two complementary halves — one to each party. Anyone with a HexaEight identity can ask for "the ASK that lets me talk to " and receive their half. The recipient asks the platform for their own complementary half. Inversion of classical PKI: Classical: Alice needs Bob's public key BEFORE encrypting. HexaEight: Alice asks platform for "the ASK that lets me talk to Bob." Done. Why the platform cannot decrypt: - Platform combines each party's password (which it does NOT store cleartext and CANNOT reverse) with SHAKE-256-derived material. - Output is two halves of a shared secret. - Platform sees the derivation inputs but never the underlying password. - SHAKE-256 is one-way. Cache shape (two-way indexed): - By (recipient, kgt) for direct peer lookups. - By sha256(sessionId) for Sessioned envelopes. - Pinning marks an entry as preferred so cache pressure cannot evict it. --- ## 8. CONCEPT — KGT (KEY GENERATION TIME) long nowMinutes = (long)(DateTime.UtcNow - DateTime.UnixEpoch).TotalMinutes; long kgt = nowMinutes - (nowMinutes % 15); 15-minute window. Three purposes: - Clock skew tolerance (<15 min skew → same KGT). - Natural key rotation (leaked ASK only useful within its window). - Replay window (receivers can reject too-old envelopes). Override by passing kgt explicitly to EncryptEnvelopeAsync or FetchAskAsync. --- ## 9. WIRE FORMAT SPECIFICATION (v1) ### Encoding rules - UTF-8 string. ASCII for the routing portion. Base64URL for ciphertext. - No leading/trailing whitespace. Decoder MUST reject if present. - No quoting, no escaping. No '\n', '\r', or NUL anywhere in the envelope. - Length is unbounded. Practical ceiling ~333 MB envelope (~250 MB plaintext × 1.49 ratio). ### Reference parser (pseudocode) function parse(envelope): if length < 6: error if envelope.startsWith("hsha:"): rest = envelope[5:] pipe = rest.indexOf("|") if pipe != 64: error return Sessioned(sessionHash=rest[0:64], ciphertext=base64UrlDecode(rest[65:])) else: p1 = envelope.indexOf("|") p2 = envelope.indexOf("|", p1+1) if p1 < 1 or p2 < p1+2: error return Standard( sourceId=envelope[0:p1], kgt=parseInt(envelope[p1+1:p2]), ciphertext=base64UrlDecode(envelope[p2+1:]) ) ### Ciphertext internal layout Offset 0: 2 bytes Version tag (V3 or V39 mode flag) Offset 2: 32 bytes HMAC-SHA256 integrity tag Offset 34: n bytes Encrypted block stream Inside the decrypted ciphertext is a JSON object with at minimum SENDER, RECEIVER, BODY. The Bridge surfaces SENDER as DecryptedEnvelope.Sender — cryptographically verified. ### Conformance for new SDK implementations 1. Encrypt a Variant B envelope decryptable by the .NET Bridge given matching session. 2. Decrypt a Variant B envelope from the .NET Bridge given matching session. 3. Surface SENDER from inside the encrypted JSON as a top-level field. 4. Reject envelopes with leading/trailing whitespace, NULs, embedded newlines, or unknown variant prefixes. --- ## 10. BRIDGE API REFERENCE — EVERY METHOD Namespace: HexaEight.Bridge Class: Client ### Constructor new Client() Loads identity from env-file + hexaeight.mac (or hexaeightkeys.db on Azure). Authenticates to the platform. ### Identity properties string Name The HEXAEIGHT_RESOURCENAME (e.g. agent01.yourdomain.com). This is the value other parties pass as `recipient`. static string BridgeVersion Bridge NuGet version (e.g. "1.0.0-preview9"). static string TargetFramework ".NETCoreApp,Version=v8.0" | "v9.0" | "v10.0". ### Envelope encryption Task EncryptEnvelopeAsync( string recipient, string body, long? kgt = null, string? pinAsk = null, string? sessionId = null) Encrypts a string body into a single envelope string. recipient Required. Recipient's Name. body Required. UTF-8 plaintext. Tested to 250 MB. kgt Optional. Override the KGT. Default: current 15-min window. pinAsk Optional. Use a specific pre-fetched ASK. sessionId Optional. If set, emits Variant B and caches ASK under sha256(sessionId). USE THIS — Sessioned is the supported mode. Returns: envelope string, or empty string on failure. Task DecryptEnvelopeAsync(string envelope, string? pinAsk = null) Auto-detects variant by "hsha:" prefix. Returns DecryptedEnvelope. static InspectedEnvelope InspectEnvelope(string envelope) Parses public metadata without decrypting. For deny-list filtering. ### ASK cache Task FetchAskAsync(string recipient, long? kgt = null) Cache hit returns cached. Miss triggers platform fetch and caches as non-pinned. void PinAsk(string recipient, long kgt, string ask) Marks the (recipient, kgt) cache entry as pinned (3 args — ask required). void PinAskForSession(string sessionId, string ask) Marks the session cache entry as pinned. void UnpinAsk(string recipient, long kgt) Removes the cache entry for (recipient, kgt). (kgt required.) void UnpinAskForSession(string sessionId) Removes the cache entry for sha256(sessionId). bool HasCachedAsk(string recipient, long kgt) Test cache presence without fetching. bool HasCachedSession(string sessionId) Test cache presence without fetching. void ClearAskCache() Wipe entire cache (both recipient-keyed and session-keyed). No args. ### Persistence Task SaveAskCacheToDiskAsync(string filePath) Serialize cache to JSON file. Contains ASK material in plaintext — protect with chmod 600 / restricted ACLs. Task LoadAskCacheFromDiskAsync(string filePath) MERGES entries into in-memory cache (does NOT overwrite). Call ClearAskCache() first if you want a clean load. Throws FileNotFoundException if missing. Task EnableAutoPersistAsync(string filePath, bool loadIfExists = true) Every cache mutation triggers a 2-second-debounced async write to filePath. If loadIfExists is true and file exists, loads it synchronously first. void DisableAutoPersist() Performs final synchronous flush and stops auto-writes. ### Encrypted JWT (handshake / identity assertion) bool AddPayloadItem(string key, object value) Attach a custom claim to the next JWT. Use for message BODY, SESSIONID, or any tamper-protected metadata. WARNING: custom claims PERSIST across CreateEncryptedJwt calls — only the standard claims are refreshed each time. Always call ClearAllPayloadItems() before building an unrelated JWT. bool ClearAllPayloadItems() Remove every claim (standard + custom) from the pending payload. string ViewPayloadString() Return the pending payload as JSON for inspection. Diagnostic only. Task CreateEncryptedJwtTokenUsingSharedKeyAsync( string audience, int expiryMinutes, long kgt, string sharedKey) Produce a HexaEight-encrypted JWT. The plaintext header contains: - issuer = sender's Name (Bob reads this with zero crypto) - kgt = current key window - kid = DDE-encrypted random signing key - typ, channelSecurityContext, iat, exp, unsecured The payload is DDE-encrypted under sharedKey. Signature is HS256 over the random signing key locked inside `kid`. Task ValidateTokenUsingSharedKeyAsync(string token, string sharedKey) Verify HS256 signature, decrypt `kid` to recover signing key, decrypt payload. Returns claims JSON on success. Returns a string starting with "-" on failure (bad signature, wrong ASK, expired, etc). The `iss` claim in the returned JSON is the cryptographically guaranteed sender identity. TRUST THIS, not the plaintext header `issuer`. ### Lower-level methods (protocol integration) Task FetchSharedKeyDirectAsync(string recipient, long kgt) Raw ASK fetch with no caching. Task EncryptMessageUsingSharedKeyAsync(string recipient, string message, string sharedKey) Encrypt under caller-supplied ASK. Task DecryptMessageUsingSharedKeyAsync(string encryptedMessage, string sharedKey) Decrypt under caller-supplied ASK. Task VerifyEnvironmentAsync() Returns true if identity files are valid for current mode. --- ## 11. RECORDS AND ENUMS record DecryptedEnvelope( string SourceId, // sender's on-wire prefix (Variant A) — empty for Variant B long? Kgt, // KGT used (Variant A) — null for Variant B string Sender, // sender's Name, cryptographically verified from inner JSON string Body, // decrypted plaintext body bool FromSession) // true if envelope was Variant B THE TRUSTWORTHY SENDER FIELD IS `Sender`, NOT `SourceId`. Sender is from inside the encrypted JSON — impossible to forge without breaking the V4 trapdoor and HMAC simultaneously. record InspectedEnvelope( string? SourceId, // (Variant A only) long? Kgt, // (Variant A only) string? SessionHash, // (Variant B only — 64 hex chars) EnvelopeKind Kind) // Standard | Sessioned record AskEntry( string Ask, bool Pinned, DateTime FetchedAtUtc) enum EnvelopeKind { Standard, Sessioned } --- ## 12. ERROR SEMANTICS InvalidOperationException ASK fetch failed, decryption returned empty, or sessioned envelope has no cached ASK for the session. FormatException Malformed envelope (wrong number of '|' parts, invalid Base64URL, NULs, etc). FileNotFoundException LoadAskCacheFromDiskAsync called on missing file. ArgumentNullException Null/empty path passed to a persistence method. Every async method is implemented as Task.Run over the underlying synchronous HexaEight library. This sidesteps the JSSynchronizationContext deadlock when called from Node.js via node-api-dotnet. You can safely await from any sync context. --- ## 13. PERFORMANCE Operation Typical -------------------------------------- ------- Cold FetchAskAsync (network) ~ 1–20 s Cache hit FetchAskAsync < 5 ms Encrypt 1 KB envelope (cache-warm) ~ 150 ms Decrypt 1 KB envelope ~ 150 ms Encrypt / decrypt 100 MB ~ 2.5 s each Encrypt / decrypt 250 MB ~ 6 s each Above 250 MB, host process memory pressure dominates; streaming planned. --- ## 14. CROSS-LANGUAGE NAMING MAP .NET Node.js Python (planned) ---------------------------------------- ------------------------------------ ----------------------------------- new Client() await HexaEight.connect() await HexaEight.connect() EncryptEnvelopeAsync(...) he.envelope.encrypt(...) he.envelope.encrypt(...) DecryptEnvelopeAsync(...) he.envelope.decrypt(...) he.envelope.decrypt(...) Client.InspectEnvelope(...) [static] HexaEight.inspectEnvelope(...) HexaEight.inspect_envelope(...) FetchAskAsync(...) he.ask.fetch(...) he.ask.fetch(...) PinAsk(recipient, kgt, ask) he.ask.pin(recipient, kgt, ask) he.ask.pin(recipient, kgt, ask) PinAskForSession(sessionId, ask) he.ask.pinForSession(sessionId, ask) he.ask.pin_for_session(sessionId, ask) UnpinAsk(recipient, kgt) he.ask.unpin(recipient, kgt) he.ask.unpin(recipient, kgt) UnpinAskForSession(sessionId) he.ask.unpinForSession(sessionId) he.ask.unpin_for_session(sessionId) HasCachedAsk(recipient, kgt) he.ask.has(recipient, kgt) he.ask.has(recipient, kgt) HasCachedSession(sessionId) he.ask.hasSession(sessionId) he.ask.has_session(sessionId) ClearAskCache() he.ask.clear() he.ask.clear() SaveAskCacheToDiskAsync(path) he.ask.saveToDisk(path) he.ask.save_to_disk(path) LoadAskCacheFromDiskAsync(path) he.ask.loadFromDisk(path) he.ask.load_from_disk(path) EnableAutoPersistAsync(path, lif) he.ask.enableAutoPersist(path, opts) he.ask.enable_auto_persist(path, opts) DisableAutoPersist() he.ask.disableAutoPersist() he.ask.disable_auto_persist() Every Task becomes a Promise in JS and an awaitable in Python. Every record becomes a plain object / dict. camelCase in JS/TS, snake_case in Python. --- ## 15. GUIDES ### JWT handshake (the recommended opening move) The first message in any conversation. JWT carries body + sessionId + identity proof in one tamper-proof artifact. Bob needs no out-of-band hint about who sent it. // ── Alice initiates ───────────────────────────────────────── var alice = new Client(); string bob = "bob.example.com"; string sid = Guid.NewGuid().ToString(); long kgt = CurrentKgt(); string ask = await alice.FetchAskAsync(bob, kgt); alice.PinAskForSession(sid, ask); alice.ClearAllPayloadItems(); alice.AddPayloadItem("BODY", "first message"); alice.AddPayloadItem("SESSIONID", sid); string jwt = await alice.CreateEncryptedJwtTokenUsingSharedKeyAsync(bob, 5, kgt, ask); // send jwt over any transport // ── Bob receives ──────────────────────────────────────────── var bob = new Client(); // Parse plaintext header (no crypto) — learn issuer + kgt var hdr = ParseJwtHeader(jwt); string claimedSender = hdr.GetProperty("issuer").GetString(); long k = hdr.GetProperty("kgt").GetInt64(); string ask = await bob.FetchAskAsync(claimedSender, k); string claimsJson = await bob.ValidateTokenUsingSharedKeyAsync(jwt, ask); var c = JsonDocument.Parse(claimsJson).RootElement; string verifiedSender = c.GetProperty("iss").GetString(); // cryptographic string sid = c.GetProperty("SESSIONID").GetString(); string body = c.GetProperty("BODY").GetString(); bob.PinAskForSession(sid, ask); // ready for steady-state ### Agent-to-agent steady state (after the JWT handshake) // Bob replies on the same session string reply = await bob.EncryptEnvelopeAsync(verifiedSender, "pong", sessionId: sid); // Alice receives — cache already has the matching ASK pinned var msg = await alice.DecryptEnvelopeAsync(reply); // msg.Sender == "bob.example.com" (verified) // msg.Body == "pong" // msg.FromSession == true // Alice sends a follow-up string env = await alice.EncryptEnvelopeAsync("bob.example.com", "more", sessionId: sid); ### Sessioned (steady-state) // No more FetchAskAsync needed — the session-pinned ASK is reused var env = await alice.EncryptEnvelopeAsync(bob, "message N", sessionId: sid); // Session rotation alice.UnpinAskForSession(oldSid); bob.UnpinAskForSession(oldSid); string newSid = Guid.NewGuid().ToString(); // agree out of band, fetch fresh ASKs ### Cache persistence // Manual await client.SaveAskCacheToDiskAsync("./ask-cache.json"); client.ClearAskCache(); // before load if you want a clean state await client.LoadAskCacheFromDiskAsync("./ask-cache.json"); // Auto (2-second debounced) await client.EnableAutoPersistAsync("./ask-cache.json", loadIfExists: true); client.DisableAutoPersist(); // final flush at shutdown WARNING: Cache file contains derived ASK material. Protect with OS-level permissions: chmod 600 ./ask-cache.json # Unix icacls .\ask-cache.json /inheritance:r /grant:r "%USERNAME%":F # Windows ### Swarm collaboration (one identity, N machines) The unique HexaEight pattern. N machines all running the same identity name (each with its own activation + hexaeight.mac + activation password) collaborate via a shared SWARM KEY. Each machine independently derives its own byte-different but cross-functional ASK from (identity, Swarm Key). KEY DISTINCTION: Swarm Key vs Activation Password are DIFFERENT secrets. Activation password — per-machine, set during hexaeight-activate. Swarm Key — shared across all swarm machines, arbitrary strong password the swarm members agree on. Distribute via JWT handshake. Confusing the two is a common mistake — they are unrelated. MECHANISM: Each machine calls: ask = await client.FetchInternalKeyAsync(swarmKey) - Platform derives the per-machine ASK from machine activation + swarm key - ASKs are byte-different per machine - ASKs are cross-functionally compatible: machine A's ASK can decrypt what machine B's ASK encrypted, and vice versa - ASKs are TIME-STABLE (no KGT window) — same Swarm Key produces the same effective encryption forever - Platform sees the request but does NOT store or learn the Swarm Key ONBOARDING A NEW MACHINE: 1. New machine activates as the swarm identity (own hexaeight-activate) 2. New machine does JWT handshake with an existing swarm member 3. Existing member sends Swarm Key inside the JWT body (tamper-proof) 4. New machine calls FetchInternalKeyAsync(swarmKey) and can immediately read all existing swarm-encrypted state USE CASES: - Horizontally-scaled AI agent (N workers, one identity, shared task queue) - Multi-region replicas (us-east encrypts, eu-west decrypts) - Failover (primary crashes, standby reads same swarm-encrypted state) - Encrypted task queues (producers and consumers all swarm members) - Audit-evident multi-process pipelines - Shared policy/IAM state (Casbin policy in swarm-encrypted file) WHAT YOU LOSE WITHOUT THE SWARM KEY MECHANISM (per-KGT FetchAsk alternative): - Platform stops issuing ASKs for a KGT after ~1 hour - New machines joining the swarm later cannot get ASKs for old KGTs - Archived messages need message-to-KGT bookkeeping - All members have to agree on a rotating KGT every 15 minutes WHAT THE PLATFORM SEES: - Per-machine activations, per-machine FetchInternalKeyAsync requests, machine metrics WHAT THE PLATFORM DOES NOT SEE: - The Swarm Key contents - Plaintext of any swarm-encrypted blob - Contents of your shared storage - The inter-swarm traffic itself (it travels over your transport) Even HexaEight cannot decrypt swarm-encrypted state. The Swarm Key never leaves your infrastructure. VERIFIED: Cross-machine interop has been tested between a Windows installation and WSL2 Ubuntu installation of the same identity. The two machines' ASKs differ byte-for-byte but each successfully decrypts what the other encrypted. CODE PATTERN: // Any machine in the swarm: var client = new Client(); string swarmKey = "swarm-shared-secret-2026-x9z"; // distributed via JWT handshake string ask = await client.FetchInternalKeyAsync(swarmKey); // Encrypt for any swarm member (including future joiners): var jwt = new JWT(true); string ct = jwt.HEClient.EncryptMessageUsingSharedKey(client.Name, body, ask); File.WriteAllText("/shared/state.enc", ct); // any shared storage // On any other swarm machine: string ct = File.ReadAllText("/shared/state.enc"); string body = jwt.HEClient.DecryptMessageUsingSharedKey(ct, ownSwarmAsk); SINGLE-WRITER CONSTRAINT FOR SHARED MUTABLE STATE: Concurrent encrypted reads are safe. Concurrent encrypted writes corrupt the file. Use ETag (S3 If-Match) or cooperative file locks (shipped in Bridge preview15 via SwarmLockManager) for mutable shared blobs. --- ## 15.5 AUTHORIZATION (IAM) — Bridge preview14+ (single-agent), preview15+ (swarm) HexaEight.Bridge ships a built-in authorization layer that gates every encrypted message by cryptographic identity. Four-tier policy hierarchy, Casbin-backed evaluation, swarm-shareable encrypted policy storage, multi-party set-membership rules, multi-hop relay support. ### SOURCEID — how the sender is identified When an agent activates via `hexaeight-activate`, the HexaEight platform issues a login token persisted to env-file. Client.SourceId is the opaque base64 segment before the first "." of that token. Bridge.Client.SourceId → "ww7YZRMeu4DlulCvrENpK652d4n6pVbdAiyhDfZvl0vs..." ↑ ~2000+ chars, safe to transmit in plaintext Wire envelope format (Variant A): {sender-SourceId}|{kgt}|{ciphertext} Wire envelope format (Variant B — sessioned): hsha:{sha256(sessionId)}|{ciphertext} Authorization flow: 1. Sender reads its own SourceId from login token 2. Sender calls FetchAskAsync(recipient, kgt) → gets sender's half of shared key 3. Sender encrypts; embeds own SourceId in wire envelope 4. Receiver parses SourceId from wire 5. Receiver calls FetchAskAsync(srcId=SourceId, kgt) → gets receiver's half 6. Receiver decrypts; extracts SENDER field from inner JSON (set by platform at encrypt time, cryptographically unforgeable) 7. IAM uses the verified SENDER field (NOT the wire-level SourceId) for policy evaluation VERIFIED: cross-identity messaging tested end-to-end with three distinct activated identities (radar84 → back65 → faith57 multi-agent relay). ### 4-TIER POLICY HIERARCHY (deny-overrides; any deny in any tier denies) | Tier | Realm key | Fires when | |-------------|--------------------------|---------------------------------------------------------| | 1 User | user: | A user identity (email) is sender, dest, or claim | | 2 Session | session: | Envelope.SessionId matches | | 3 App | app: | ProtectionHash matches (or resolves via app-registry) | | 4 Default | default | Always evaluated | Tier 1 uses EFFECTIVE identities: effective_sender = OriginalSender ?? Sender; effective_dest = FinalRecipient ?? WireDest. Other tiers use the cryptographic wire sender. ### POLICY CSV SCHEMA p, SENDER, DESTINATION, REALM, DIRECTION, EFFECT SENDER: cryptographic identity, wildcards (*) or globs (?) DESTINATION: identity, "self", or mp:[a,b,c] for multi-party set match REALM: default | app:X | session:Y | user:Z DIRECTION: inbound | outbound EFFECT: allow | deny Wildcards use Casbin's built-in globMatch. ### AUTOENFORCE FLAG Default: ON. When ON: - EncryptEnvelopeAsync runs outbound check; throws UnauthorizedAccessException on deny - DecryptEnvelopeAsync runs inbound check; replaces body with "redacted due to authorization enforcement" on deny - Authorized field on DecryptedEnvelope: Disabled / Allowed / Denied When OFF (preview13-compatible behavior): - Standard Encrypt/Decrypt skip checks - Apps can opt into enforcement via: client.AuthorizeAsync(ciphertext) -- explicit inbound enforcement client.CheckOutboundAsync(...) -- explicit outbound pre-check These mutex with AutoEnforce (throw when flag is ON). ### RELAY ENVELOPES (multi-hop, no impersonation) FinalRecipient — sender claims "this is for X"; set on outbound encrypt OriginalSender — relay claims "this came from X"; set by relay agent ProtectionHash — sender's SHA-512 executable hash; auto-populated MultiPartyChain — set of recipient identities in sequential-peeling chain Wire-level cryptographic sender is ALWAYS the actual sender (no impersonation). Relay agents call: client.CreateRelayEnvelopeAsync( originalSender: msg.Sender, finalRecipient: msg.FinalRecipient, body: msg.Body); Multi-hop relay supported: body passes through unchanged so HexaEight content signatures survive every hop end-to-end. Verified end-to-end with three identities (radar84 → back65 → faith57). ### SWARM MODE (preview15+) await client.EnableSwarmModeAsync(swarmKey, quorumStorageRoot) IRREVERSIBLE per agent. Once entered, agent cannot return to single-agent without wiping hexaeight.mac + env-file + policy file (cryptographic one-way: internal/self key replaced by Swarm Key as SKEY parameter). Requires non-empty policy (refuses to enter swarm in bootstrap mode). Once active: - policy.enc HexaEight-envelope-encrypted under Swarm Key at quorumRoot - all swarm members with same Swarm Key see the same policy - client.SwarmLockManager.TryAcquireLockAsync(name) for distributed locks (3-min TTL, refresh required for longer holds, encrypted lock files) - FileSystemWatcher detects changes by other swarm members → auto-reload - cloud storage adapters live in separate hexaeight-storage-adapters repo Cross-machine verified: Windows wrote encrypted policy.enc; WSL read same file, decrypted via Swarm Key, enforced identically. ### EXTERNAL AUTHZ FRAMEWORK INTEGRATION Default is Casbin. Bridge exposes IAuthorizationProvider for external systems: public sealed class MyLdapProvider : IAuthorizationProvider { public Task AuthorizeAsync(...) { ... } public Task ReloadAsync() { ... } public Task NotifyPolicyChangedAsync(string reason) { ... } public event EventHandler PolicyChanged; public bool IsInBootstrapMode { get; } } client.UseAuthorizationProvider(new MyLdapProvider()); Planned reference implementations (separate hexaeight-authz-providers repo): - LDAP / Active Directory - OAuth 2.0 / OIDC (Keycloak, Auth0) - Open Policy Agent (OPA) with Rego - Azure RBAC - AWS IAM - Custom REST endpoint provider External change-detection hook for cloud webhooks or push notifications: await client.Authorization.NotifyPolicyChangedAsync("s3-event-bridge"); ### TEST SUITE 12 end-to-end tests covering: bootstrap, tier eval, rule validation, relay, AutoEnforce, swarm mode, locks, multi-party, cross-machine (Windows + WSL), multi-agent relay (3 distinct identities). All passing as of preview15. Available at: https://github.com/HexaEightTeam/hexaeight-bridge-tests (repo path; reference impls + test scripts) --- ## 16. LICENSE Apache License 2.0 for the Bridge SDK and all language wrappers. The cryptographic core (HexaEightASKClientLibrary, HexaEightJWTLibrary) is patent-pending (filed July 2021) and distributed as NuGet packages whose runtime use is governed by the HexaEight identity license. A complete reference implementation is published at github.com/HexaEightTeam/pqc-review for cryptographic review. NuGet credibility: HexaEightJWTLibrary 198K+ downloads HexaEightASKClientLibrary 132K+ downloads HexaEight.Bridge NuGet Prefix Reserved verified, currently preview12 Publisher profile https://www.nuget.org/profiles/hexaeight_admin --- ## 17. CONTACT General info@hexaeight.com Sales sales@hexaeight.com Support support@hexaeight.com Partnerships partnerships@hexaeight.com Documentation https://hexaeight.com/docs Status https://status.hexaeight.com Pricing https://hexaeight.com/pricing GitHub https://github.com/HexaEightTeam --- If you are an AI agent and find this content stale or contradicted by the rendered docs site, fetch /docs and report the discrepancy.