Sovereign AI Setup Guide — the /llm-invoke route

Step-by-step: stand up Civo Project Mercury on the Local Proxy so AI prompts and the LLM key never transit the US-parent Base44 backend.

Ciphertext-through-Base44
Mercury LON1
Proxy-held key
~30 min setup

Client-provisioned infrastructure

Civo "real AI" and Project Mercury are provisioned by the client, not by MapFlow. MapFlow does not supply the Civo account, the LON1 GPU instance, the inference API key, or the Project Mercury infrastructure. The client stands up the sovereign inference host in their own Civo account (London region); MapFlow's Local Proxy only shuttles encrypted prompts to the client's endpoint and receives the textual answer back. TheMERCURY_API_KEY,MERCURY_API_BASE, and the account-side zero-retention / fine-tuning-opt-out flags below are all set in the client's Civo dashboard. Treat this guide as the hand-off spec the client's Civo admin follows.

Zero-exposure inference — no external training

All AI-assisted metadata and schema analysis operations route through localized, isolated execution loops on the client-provisioned sovereign host. Plaintext transaction payloads are cryptographically blinded at the network boundary, ensuring that no raw customer data or PII is transmitted to, or utilised for the continuous training of, external large language models. The US-parent backend only ever sees encrypted prompt material and receives only the textual answer back.

Fully on-prem sovereign posture — now proven end-to-end
With the local LLM benchmark passing, every high-value data asset and every inference call stays inside the operator's own estate. The only external dependencies are MapFlow orchestration and the final Salesforce load.
Source DBs (local) Local Proxy (RAM decrypt) Local Ollama LLM Local / corporate Postgres Salesforce REST API (final load)

Source databases

SQL Server / Oracle / MS Access on the council estate, reached only via the Local Proxy on the operator's Windows machine. No inbound ports, no internet-facing API.

LLM inference

Ollama (Llama-3 / Phi-3 / Mistral / Qwen) bound to 127.0.0.1:11434, reached only via the proxy's /llm-invoke route. Benchmark-verified working. Zero internet egress during inference.

Postgres warehouse

Can run locally on the operator's machine or on a VM inside the corporate network (self-hosted Postgres / standalone Postgres). Point a MapFlow DBConnection at the corporate-network host. No cloud provider in the warehouse path.

MapFlow orchestration (external)

The Base44 backend — the platform — runs in the cloud and orchestrates jobs, mappings, auth, and scheduled tasks. It holds metadata only: project config, mapping definitions, job records, audit logs. It never holds the AES key or plaintext citizen data under the default local_proxy mode.

The one accepted external egress: the final load of mapped data into Salesforce, which uses the Salesforce REST API. Salesforce is the client's contracted destination — the hand-off is the documented, accepted boundary, not a residency gap in MapFlow's processing.

Source DBs local
LLM local (proven)
Postgres local / corporate
Only orchestration external
Salesforce REST = accepted hand-off

Net: the AES-256-GCM key, the plaintext citizen data, and the LLM prompts all stay inside the operator's estate. A US CLOUD Act production order against the Base44 platform returns ciphertext and non-PII metadata only — never plaintext, never LLM transcripts. Obtaining plaintext becomes a UK lawful-access matter against the operator, not a retrieval from a US provider.

What you're building
The data flow the /llm-invoke route establishes.

1. Browser → Local Proxy. The app POSTs ciphertext_inputs (ENC:-prefixed), non_pii_metadata, and a prompt_template to http://localhost:3001/llm-invoke.

2. Proxy decrypts in RAM. The proxy uses the DPAPI-held AES key to decrypt ENC: values in volatile memory — never logged, never persisted.

3. Proxy → UK LLM. The proxy fills the template, then calls Civo Project Mercury (LON1) with an API key held in its own env — not in Base44 app secrets.

4. Result → Browser. Only the parsed result returns. Base44 (and any US CLOUD Act order) sees ciphertext + non-PII metadata + the result — never the plaintext prompt or the LLM key.

Prerequisites
  • The Local Proxy (src/tools/postgres-proxy.py) already running on the operator's UK machine with the DPAPI-held AES session key loaded (the same key used for PII encryption).
  • A Civo Project Mercury account with an inference API key issued for the London (LON1) region.
  • Admin access to the MapFlow app to set llm_invocation_mode and llm_provider.
  • (Optional) A local Ollama instance at 127.0.0.1:11434 if you want the local_ollama fallback.
Step 1 — Put the Mercury key in the proxy's env (not Base44 secrets)
The LLM key lives next to the AES key on the UK machine, never on the US-parent platform.

On the machine running the proxy, set three environment variables before starting it:

# Windows (PowerShell, current session)
$env:MERCURY_API_KEY  = "mw-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$env:MERCURY_API_BASE = "https://inference.lon1.mercury.civo.com"
$env:MERCURY_MODEL_ID = "mercury-edge-30b"

# Then start the proxy (it picks the key up from env)
python postgres-proxy.py --key-file C:\\keys\\aes-key.txt

# Linux / macOS
export MERCURY_API_KEY="mw-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export MERCURY_API_BASE="https://inference.lon1.mercury.civo.com"
export MERCURY_MODEL_ID="mercury-edge-30b"
python3 postgres-proxy.py --key-file /etc/mapflow/aes-key.txt

LON1 pin enforced in code. The proxy's sovereign_llm_invoke refuses a MERCURY_API_BASE that looks generic (bare civo.com / civo.cloud). It must carry a regional/mercury host so the handshake never exits UK borders.

Step 2 — Set the Civo account-side flags (mandatory)
Code can't enforce these — the operator sets them in the Civo dashboard. They are prerequisites for the posture, not optional.
  • Toggle OFF Data Retention — flag the inference profile for zero-retention logging so no prompt/response is persisted.
  • Opt-OUT of Fine-Tuning Pools — inputs are used only for real-time inference and never saved to disk caches for future model refinement.

These satisfy ICO / UK GDPR guidelines for sovereign inference. Without them Mercury is "on Civo" but not sovereign.

Step 3 — Configure MapFlow's AI Residency card
Admin → AI Residency. The setting that activates the sovereign path.
  • LLM enabled: ON (the master kill switch — was OFF by default).
  • LLM provider: civo_mercury.
  • LLM invocation location: Local Proxy (recommended) — the default. This makes backend AI functions refuse to assemble prompts; the browser drives them via callLlmViaProxy.
  • Decryption location: Local Proxy (keep default).
  • Encryption location: Local Proxy (keep default).
  • Allowed models: add mercury-edge-30b (and mercury-frontier if you use the 256B variant).
  • Per-feature gates: enable Deep PII Scan / Follow-Me only when you accept that their prompts carry row-level data (now decrypted on the proxy, not the backend).

With llm_invocation_mode = local_proxy, the shared callLlm() throws if a backend function tries to invoke an LLM — so the plaintext prompt can never be assembled in Base44 memory. The frontend must use callLlmViaProxy instead.

Step 4 — Call the proxy from the browser
Each AI feature's frontend uses callLlmViaProxy instead of its backend function.

The browser-side caller lives at src/lib/callLlmViaProxy.js. A feature passes ciphertext + non-PII metadata + a template; the proxy fills the template after decrypting:

import { callLlmViaProxy } from "@/lib/callLlmViaProxy";

// PII classification example — column value arrives as ENC: ciphertext
const result = await callLlmViaProxy({
  proxyUrl: "http://localhost:3001",
  promptTemplate:
    "Classify the PII category of this column value. " +
    "Column: {column_name} (type {column_type}). Value: {sample_value}. " +
    "Return JSON {category, confidence}.",
  ciphertextInputs: {
    sample_value: rowSampleValue,   // "ENC:..." — decrypted in proxy RAM
  },
  nonPiiMetadata: {
    column_name: "national_insurance_number",
    column_type: "varchar(12)",
  },
  responseJsonSchema: {
    type: "object",
    properties: {
      category: { type: "string" },
      confidence: { type: "number" },
    },
  },
});
// result === { category: "government_id", confidence: 0.97 }

Placeholders {column_name}, {sample_value} are substituted on the proxy after decryption — so the browser never holds the plaintext either.

Step 5 — Verify the path
  1. curl http://localhost:3001/health — proxy up, key loaded, memory bounded.
  2. POST a smoke test to the route (plaintext passthrough, no ENC: needed to confirm Mercury answers):
    curl -X POST http://localhost:3001/llm-invoke \
      -H "Content-Type: application/json" \
      -d '{"prompt_template":"Reply with the single word PONG.","non_pii_metadata":{}}'
  3. Run the InfoSec check panel — the "AI invoked outside UK / plaintext in backend memory" checks should pass when llm_invocation_mode = local_proxy.
  4. Trigger a feature that uses callLlmViaProxy and confirm the proxy log shows [/llm-invoke] handling the call (no Base44 backend function is hit).
Fallback — local Ollama (no network egress)
If Mercury creds are absent, the route falls back to the local Ollama instance on loopback.

If MERCURY_API_KEY / MERCURY_API_BASE are unset, sovereign_llm_invoke calls local_llm_generate at 127.0.0.1:11434 — zero network egress beyond the UK machine. Set the MapFlow provider to local_ollama to use this path deliberately.

This path runs fully air-gapped: the Ollama host never needs a route to the public internet during inference (model weights are pulled once on a separate jump host and transferred across). On a Windows operator machine, Ollama installs as a native service bound to loopback — see the Local Windows LLM Setup guide for the PowerShell procedure and proxy wiring. This is the strongest residency posture available: true LLM reasoning with zero internet egress and zero cloud dependency.