JHILKE झिल्के

Just Hidden In-band Legitimate Key Exchange

Steganographic key lifecycle management for the Yakmesh mesh.

v1.0.0

The Sound of Crickets

In Nepali, झिल्के (jhilke) describes the chirping of crickets in the Himalayan night — a chorus only those who belong can hear. Outsiders perceive noise; insiders hear signals.

Like the D-Day cricket clickers, where Allied paratroopers used a simple click-clack pattern to identify friend from foe in darkness. Like the Navajo codetalkers whose language was unbreakable because it was meaningful only to those who grew up with it.

JHILKE hides its key management signals inside ordinary mesh entropy traffic. To an observer, it's noise. To nodes sharing the same codebase — it's a conversation.

Why Fireflies?

Fireflies (Lampyridae) are nature's original steganographers. Each species flashes a unique pattern — a species-specific dialect that only potential mates of the same species can decode. To every other creature in the meadow, it's just random light. Sound familiar?

JHILKE nodes do exactly what fireflies do: they embed signals in the noise of the night. The "dialect" is derived from the shared codebase hash. Same code = same species = you understand the flash pattern. Different code = different species = it's just entropy.

Real fireflies are disappearing worldwide due to light pollution, habitat loss, and pesticides. At least 2,000 species exist, with some already critically endangered. If you see fireflies near your home, consider yourself lucky — and keep the lights low for them.

Overview

JHILKE provides two critical functions that together eliminate all plaintext cryptographic exchange on the Yakmesh network:

Deterministic Bootstrap

Both nodes independently derive the same initial encryption key from the shared code hash. Traffic is encrypted from message #1 — no plaintext KEM exchange ever occurs.

Steganographic Rekey

Ongoing key rotations are coordinated via hidden signals embedded in mesh_entropy messages. No explicit REKEY messages — an observer sees only noise.

Ternary State Machine

Three-phase coordination using TRIBHUJ trits: PREPARE (+1), READY (0), SWITCH (-1). Both sides must agree before any key change occurs.

SST Modulation

Signal encoding is modulated by the Fibonacci 24-cycle (Sacred Sequence Theory), adding rotational variety that makes pattern analysis impossible.

The Problem JHILKE Solves

Traditional key exchange protocols expose critical metadata to passive observers:

Leaked Information Before JHILKE After JHILKE
KEM public keys Plaintext on wire Encrypted (bootstrap channel)
KEM ciphertext Plaintext on wire Encrypted (bootstrap channel)
Rekey timing Visible REKEY messages Hidden in entropy traffic
Key rotation frequency Countable Indistinguishable from noise
First message encryption Delayed until KEM completes Immediate (deterministic key)

Deterministic Bootstrap

When two nodes connect, they both independently compute the same initial symmetric key:

// Both nodes independently derive the same bootstrap key
// Key = HKDF(SHA3-256, codeHash, sortedNodeIds, bootstrapInfo, 32)

import { JhilkeCoordinator } from './mesh/jhilke.js';

const jhilke = new JhilkeCoordinator({
  codeHash: oracle.selfHash,   // Validation Oracle code hash
  nodeId: myNodeId,
  annex: annexInstance,
  mesh: meshInstance,
});

// Derive bootstrap key for a peer (deterministic — both nodes get same key)
const bootstrapKey = jhilke.deriveBootstrapKey(peerNodeId);

// Create ANNEX session using bootstrap key (no KEM exchange needed!)
annex.bootstrapSession(peerNodeId, bootstrapKey);

// Traffic is now encrypted from the very first message.
// KEM upgrade happens automatically through the encrypted channel.
Security Note: The bootstrap key is derived from the code hash + node IDs. Anyone with the source code and both node IDs could theoretically compute it. This is by design — the bootstrap key is a temporary bridge that protects the KEM exchange itself. It's replaced by a proper ML-KEM-768 key within seconds.

Key Derivation

// Salt includes both node IDs (sorted for order-independence)
const [first, second] = [nodeA, nodeB].sort();
const salt = `YAKMESH-JHILKE-BOOTSTRAP-2026:${first}:${second}`;

// HKDF derivation
const key = HKDF(
  SHA3-256,                              // Hash function
  codeHash,                              // Input key material (oracle hash)
  salt,                                  // Salt (sorted node IDs)
  'yakmesh-jhilke-bootstrap-key-v1',     // Info string (versioned)
  32                                     // Output: 256-bit key
);

Encryption Lifecycle

JHILKE manages the complete ANNEX encryption lifecycle:

  1. 1
    Bootstrap — Both nodes derive deterministic key from shared code hash. ANNEX session created with immediate encryption capability.
  2. 2
    KEM Upgrade — ML-KEM-768 key exchange happens inside the bootstrap-encrypted channel. KEM public key + ciphertext never appear in plaintext.
  3. 3
    Steady State — ANNEX uses the KEM-derived key. JHILKE tick loop monitors for rekey needs (every 5 minutes or 10,000 messages).
  4. 4
    Cricket Rekey — When rekey is needed, JHILKE coordinates both sides via steganographic signals. Only after mutual confirmation does the actual KEM rekey occur.

Steganographic Signals

The Firefly Flash Pattern: Just as Photinus pyralis uses a specific J-shaped flash lasting exactly 0.5 seconds followed by a 2-second pause, JHILKE nodes embed precisely-timed signals in the constant stream of mesh entropy. The pattern is meaningful only to nodes sharing the same "species" — the same codebase hash. Every other observer sees random light in the darkness.

JHILKE signals are hidden inside mesh_entropy messages — a normal part of mesh communication used for entropy exchange between peers:

// What an observer sees on the wire:
{
  type: 'mesh_entropy',
  entropy: 'a7f3b2c1...',   // 32 bytes of genuine entropy
  jhilke: '8e4d1f2a...',    // 8 bytes — cricket signal or random noise?
  pad: 'c3d2e1f0...',       // 16-64 bytes random padding (varying size)
  t: 1740100000000           // Timestamp
}

// To an observer: just entropy exchange, nothing unusual.
// To nodes sharing the dialect: a coordinated key rotation.

Dialect Derivation

The "dialect" is the shared secret that allows signal encoding/decoding. It's derived from the code hash — only nodes running identical code share the dialect:

// Dialect seed — same for all nodes with identical codebase
const dialectSeed = HKDF(
  SHA3-256,
  codeHash,                             // Input key material
  'jhilke-cricket-salt-2026',           // Salt
  'yakmesh-jhilke-dialect-v1',          // Info
  32                                    // 256-bit seed
);

// Signal generation — encodes intent + context
const signal = HKDF(
  SHA3-256,
  dialectSeed,                          // Key = dialect
  `${nodeA}:${nodeB}:${tick}:${fibPos}:${fibRoot}:${intent}`,
  'jhilke-signal-v1',
  8                                     // 8-byte signal
);

Signal Decoding

Decoding is brute-force over a tiny search space — trivial for nodes sharing the dialect, impossible without it:

// 3 intents × 3 tick offsets (±1 tolerance) = 9 attempts
const intents = [PREPARE, READY, SWITCH];  // +1, 0, -1
const offsets = [-1, 0, +1];               // tick tolerance

for (const offset of offsets) {
  for (const intent of intents) {
    const expected = generateSignal(peerId, tick + offset, intent);
    if (expected === receivedSignal) {
      // Decoded! We know the intent and approximate timing.
      return { intent, tick: tick + offset };
    }
  }
}
// No match → genuine entropy, not a cricket signal

Ternary State Machine

The rekey coordination uses a ternary state machine with TRIBHUJ trits:

Signal Trit Meaning Duration
PREPARE +1 (POSITIVE) "I'm preparing new key material" 3 ticks
READY 0 (NEUTRAL) "My new key is ready, waiting for you" Until peer ready
SWITCH -1 (NEGATIVE) "Switching to new key NOW" 2 ticks then execute

State Flow

  Initiator                         Responder
  ─────────                         ─────────
  idle                              idle
    │                                 │
    ├── needsRekey() ─────────────────┤
    │                                 │
  prepare ──── PREPARE (+1) ────▶   prepare
    │                                 │
    │     (3 ticks generating keys)   │
    │                                 │
  ready   ──── READY (0) ──────▶   ready
    │     ◀──── READY (0) ──────     │
    │                                 │
    │     (both confirmed ready)      │
    │                                 │
  switch  ──── SWITCH (-1) ────▶   switch
    │     ◀──── SWITCH (-1) ────     │
    │                                 │
  exchanging ── KEM rekey ──────▶  exchanging
    │     ◀── KEM response ─────     │
    │                                 │
  idle    (new KEM key active)     idle
        

Timing & Retry

Tick Loop

  • • 1-second heartbeat interval
  • • Sends cricket chirps to active peers
  • • Drives state machine transitions
  • • Scans ANNEX sessions every 30 ticks

Retry Logic

  • • Retry after 24 ticks (24 seconds)
  • • Maximum 3 retry cycles per rekey
  • • Abort after 72 ticks total (72 seconds)
  • • ±1 tick tolerance for network jitter

API Reference

jhilke.deriveBootstrapKey(peerId)

Derives a deterministic bootstrap encryption key for a peer. Both nodes compute the same key independently.

Parameters

peerId string The remote peer's node ID

Returns

Buffer  // 32-byte (256-bit) symmetric key
jhilke.initiateRekey(peerId)

Begins a steganographic rekey coordination for a peer. Called by ANNEX when needsRekey() returns true. Starts the cricket chirp sequence.

Parameters

peerId string The peer to coordinate rekey with
jhilke.handleIncoming(fromPeerId, message)

Processes incoming mesh_entropy messages, checking for hidden cricket signals. Genuine entropy passes through unchanged.

Parameters

fromPeerId string Sender's node ID
message object The mesh_entropy message (may contain jhilke field)
jhilke.start() / jhilke.stop()

Starts or stops the 1-second tick loop. The tick loop drives state machine transitions and sends cricket chirps to peers in active rekey coordination.

jhilke.getStats()

Returns operational statistics.

{
  bootstrapKeysDerived: 3,      // Bootstrap keys computed
  signalsSent: 142,             // Cricket chirps sent
  signalsReceived: 138,         // Chirps received
  signalsDecoded: 47,           // Successfully decoded (rest were genuine entropy)
  rekeyCoordinations: 12,       // Rekey coordinations started
  rekeySuccesses: 11,           // Successful key rotations
  rekeyAborts: 1,               // Timed-out coordinations
  activeSessions: 2,            // Peers tracked
  activeCoordinations: 1,       // Rekeys in progress
  globalTick: 3847              // Ticks since start
}

Security Properties

  • Zero plaintext key exchange — Bootstrap key encrypts the KEM exchange. All subsequent rekeys flow through the encrypted channel.
  • Perfect Forward Secrecy — No old key material is stored. Bootstrap keys are ephemeral (replaced by KEM within seconds). Rekeys generate fresh ML-KEM-768 keypairs.
  • Codebase-bound dialect — Only nodes with identical code (verified by the Validation Oracle) can encode or decode cricket signals. Fork the code → different dialect.
  • Race condition elimination — The ternary state machine ensures both sides agree before any key switch. No more "Unsupported state" errors during rekey.
  • Traffic analysis resistance — Cricket chirps are indistinguishable from regular mesh_entropy messages. Variable padding adds further camouflage.

Relationship to ANNEX

JHILKE is ANNEX's companion protocol — it manages the cryptographic lifecycle while ANNEX handles the actual encryption/decryption:

Responsibility ANNEX JHILKE
Symmetric encryption AES-256-GCM encrypt/decrypt
Initial key Receives from JHILKE Derives deterministic bootstrap
KEM exchange ML-KEM-768 encapsulate/decapsulate Routes through encrypted channel
Rekey trigger Detects needsRekey() Coordinates timing via chirps
Message signing ML-DSA-65 sign/verify
Design Philosophy: JHILKE and ANNEX are deliberately separate modules. ANNEX doesn't know how its keys are derived or when rekeys happen — it just encrypts. JHILKE doesn't know how encryption works — it just manages the key lifecycle. Separation of concerns, Himalayan style.