Working example of an end-to-end FHE app on the Niobium Fog — the server side runs on ciphertexts only; the client keeps its keys. Self-contained: sources, bench, and design notes all live in this directory. (See also ../ for the sibling fhe-messenger example.)
Compute φ = (1 + √5) / 2 to 10 digits entirely on encrypted data — an end-to-end smoke test for the Niobium Fog that exercises CKKS record/replay, hollow trace upload, and FHE decryption of a non-trivial iterative circuit.
What runs where
| Binary | Runs on | Does |
|---|---|---|
golden_twin |
anywhere (no OpenFHE) | plain-double reference of the exact circuit expression tree |
golden_client |
client | keygen + encrypts y₀=0.45 and the public constants 5, 3, 0.5, 2.5 |
golden_server |
local sim or Fog | records the Newton loop as a FHETCH trace, replays it |
golden_decrypt |
client | decrypts the result ciphertext and checks φ to N digits |
The Fog never sees a plaintext: the client encrypts every value the server
uses, recording runs in --hollow mode (the trace carries placeholder
values, no real math on the worker), and the only decryption happens back
at the client with the client's secret key.
The circuit
Newton's method for y = 1/√5, starting y₀ = 0.45:
y ← 0.5 · y · (3 − 5y²) (3 iterations → ~1e-16 float error)
φ = 0.5 + 2.5y
~13 randomized ciphertext×ciphertext multiplies; no rotations, no
bootstrap. Expression tree shared verbatim via circuit.h
(templated over an op policy) so the floating-point twin and the CKKS
server run the same arithmetic.
CKKS parameters
Ring 2048 for local gate testing, 65536 for the Fog. Depth 20 (naïve count
13 + headroom under FLEXIBLEAUTO), scaling 58, first modulus 60,
HEStd_NotSet (demo posture; logQ fits comfortably at N=2^16).
Build
Needs a Release-built niobium-client checkout (this project compiles against the client's vendored instrumented OpenFHE + libnbfhetch):
# once: niobium-client Release build
(cd ../../niobium-client && make release)
cmake -B build -DCMAKE_BUILD_TYPE=Release # -DNIOBIUM_CLIENT_DIR=<path> if not ../../niobium-client
cmake --build build -j
Build gotcha: every binary must be compiled with
target_compile_definitions(... OPENFHE_CPROBES) (this project sets it).
Without it the instrumented OpenFHE headers skip probe emission, the
recorded trace is silently incomplete (~10k instructions short here, most
keys unloaded) and replay reads uninitialized towers — opaque wrong
answers or a Decode failure, not a build error. This repo's CMakeLists
already handles the ordering quirk on Ubuntu (libnbfhetch must come after
the OpenFHE libs on the link line because the instrumented OpenFHE libs
need its cprobe symbols).
Run
Convenience targets (all run from build/):
make twin # floating-point twin — bit-exact at this depth
make test-golden-local # twin → client → server (record + replay) → decrypt at 10 digits
make fog-golden # + Fog round trip (needs ~/.fog/credentials)
Manual equivalent of make test-golden-local:
cd build
./golden_twin
./golden_client golden_keys 2048
./golden_server golden_keys --no-ring-dim-check
./golden_decrypt golden_keys 10 # expect [PASS]
make fog-golden additionally re-keys at ring 65536, gates through the
local simulator in hollow mode, then runs
fog submit ./build/golden_server build/golden_fog --hollow --target=FOG
and decrypts the ciphertext the Fog returns.
The server cache-name keys on ring dimension + iteration count (see
cache_parameters in server.cpp), so the stale-toy-ring trace
from the local gate is never shipped to the Fog (mixing them produces an
opaque 19-second 500 from the worker). The Makefile also purges any prior
trace project dir before recording — a leftover stale trace is silently
reused otherwise.
Verified result
Fog job 695ea230…39 (fog-worker-batch-1, 2026-09-04):
hollow trace 66,762 instructions, 206.6 MB uploaded (seven ciphertexts +
eval mult key at N=2^16), worker run ~17 s, 1.0 MB returned.
Decrypt of the Fog result: φ = 1.618033988748 — error 2.16e-12, a PASS
with ~100× margin on the 10-digit bar.
Bench
bench/ holds the Fog round-trip benchmark harness:
bash bench/fog_golden.sh [N] (or make bench-fog RUNS=N) runs N serial
end-to-end round trips and writes bench/runs/<UTC-ts>/ with
results.jsonl + REPORT.md (see bench/README.md). A verified 50/50
reference run lives there too; run dirs are not checked in.
See also
The sibling example ../fhe-messenger is an
XMPP-shaped FHE maildrop (Rust CLI + C++ kernel) with its own architecture
diagrams under its design/.
Provenance note
The source files of this example were generated by the Niobium FHE Application Design AI Assistant (FHEanna) and are BSD-3-Clause (see LICENSE); the underlying cryptography lives upstream in niobium-client (Apache-2.0) and its vendored OpenFHE build — compiling against it does not relicense any of that code.