// Copyright 2026 Niobium Microsystems, Inc. // Licensed under the Apache License, Version 2.0. // ============================================================================ // 11 โ€” PRE-ENCRYPTED INPUTS (the real production file shape) // ============================================================================ // ๐Ÿ’ก Real-world picture: a hospital encrypts patient records on its own // servers, then ships the SEALED FILES to a cloud running this kind of // analytics. The cloud computes on the encrypted bytes; it never holds a // decryption key. Even if the cloud is breached, attackers see only // encrypted blobs. This is how production FHE deployments look โ€” and this // example is the shape they use, in 70 lines. // // Example 10 moved inputs out of the source into a readable .txt file. // Real Niobium servers go one step further: inputs arrive on disk ALREADY // ENCRYPTED. The server only ever sees ciphertext bytes โ€” a client (often // a different machine, sometimes a different organisation) did the // encryption earlier. // // This example shows that exact shape: // // * First run โ€” kit acts as the "client": encrypts the values, // serialises each ciphertext to a .bin file under // .runs//.inputs/. The .bin location is // per-backend because a ciphertext is tied to the // keyset that encrypted it, and the kit uses // independent keys for FHE_SIM vs FPGA. // * Every run after โ€” kit acts as the "server": loads each .bin file // and hands it to the compiler with the FILENAME // (3-arg tag_input), the way a production // Niobium application does. // // Because the .bin files live OUTSIDE the source tree, swapping them in or // out (or deleting and re-encrypting) does NOT trigger a recompile. // // Run it: ./run.sh 11_pre_encrypted_inputs <- first run COMPILES on CPU // Run again: ./run.sh 11_pre_encrypted_inputs <- runs the cached program (RUNTIME) // Delete the .bin: rm .runs/fhe-sim/.inputs/11_pre_encrypted_inputs.*.bin // Run again: ./run.sh 11_pre_encrypted_inputs <- re-encrypts, still RUNTIME // Compile + run once: ./run.sh 11_pre_encrypted_inputs --full // Run on the FPGA: ./run.sh 11_pre_encrypted_inputs --fpga // (FPGA generates its own .bin under .runs/fpga/.inputs/) // ============================================================================ #include "starter_kit.h" using namespace niobium::starter; int main(int argc, char** argv) { Session s(argc, argv, /*name=*/"11_pre_encrypted_inputs", /*mult_depth=*/1, /*rotation_indices=*/{1, 2, 4}); // First run encrypts the literals and writes .bin files under the // per-backend .inputs/ (e.g. .runs/fhe-sim/.inputs/). Later runs load // those .bin files straight from disk. Either way, the compiler is told // the filename so it can reload on replay. auto a = s.encrypt_or_load_bin("a", {1, 2, 3, 4, 5, 6, 7, 8}); auto b = s.encrypt_or_load_bin("b", {8, 7, 6, 5, 4, 3, 2, 1}); s.compute([&] { auto cc = s.cc(); auto prod = cc->EvalMult(a, b); // element-wise product auto acc = prod; for (int shift : {1, 2, 4}) acc = cc->EvalAdd(acc, cc->EvalRotate(acc, shift)); // sum slots s.output("dot_product", acc); }); s.print_and_verify(); // For the shipped inputs (1..8 . 8..1) every slot ~= 120. return 0; } // ============================================================================ // ๐Ÿงช TRY THIS // ============================================================================ // - Delete the .bin files (`rm .runs/fhe-sim/.inputs/11_*.bin`) and run // again. The example re-encrypts fresh bytes; the compiled program is // reused โ€” RUNTIME only. // - Open one of the .bin files: `xxd .runs/fhe-sim/.inputs/11_pre_encrypted_inputs.a.bin // | head`. What you see is the on-the-wire shape Niobium customers move // between their client and server โ€” the accelerator never sees more // than this. // - Compare with example 10: same data-outside-source idea, but the data // is encrypted at rest. That's the only difference, and it's the entire // reason this pattern is production-appropriate. // - Revisit example 01 with the production cache: // ./run.sh 01_add --pre-encrypted --reset // ./run.sh 01_add --pre-encrypted --regen-input=a:100,200,300,400,500,600,700,800 // The second invocation should hit RUNTIME (not COMPILE) even though // the data changed -- the production cache contract on an example // you've already learned. See README's Phase 2 section for context. // ============================================================================