Skip to main content

Encrypted compute layer for AI agents

Project description

NXD 0.5.1

NXD is an encrypted compute layer for AI agents. It wraps fully homomorphic encryption, credential vaulting, privacy primitives, and operator-only reveal flows behind a single Python import so developers can run agents on sensitive data without exposing client records, credentials, or proprietary code to models, clouds, or MCP servers.

Three guarantees

  1. The agent works fully - capability unchanged. The agent still completes its task.
  2. The agent sees nothing - when shield() or vault.safe_use() is used correctly. The agent receives ciphertext or callback results, not the secret itself. A callback that returns a credential defeats this guarantee.
  3. The operator holds the keys - in local mode, the unlock secret lives in the device OS keychain and NXD only accesses it through an explicit operator session. In hosted mode, key custody moves to HashiCorp Vault. Local machine compromise still defeats local mode.

Install

pip install nxd==0.5.1
nxd init-operator
nxd open

Requires Python 3.10 or 3.11 for the FHE path (concrete-ml).

Operator Setup

Step 1 — Initialize your operator key

nxd init-operator

NXD generates a 24-word operator key, shows it once, then clears it from the screen. Write it down. NXD does not store the words.

Step 2 — Open the vault

nxd open

Your device keychain handles the day-to-day unlock. On macOS this means Keychain Access and the system password or Touch ID gate the stored operator key. NXD never sees the device password.

Step 3 — Lock when done

nxd lock

Sessions last 8 hours by default unless you lock earlier.

How key entry works

When you confirm or recover the 24 words, nothing appears as you type. No dots. No asterisks. The input is captured but never echoed.

Legacy fallback

NXD_OPERATOR_PASSPHRASE is still accepted for backwards compatibility and migration, but the recommended path is nxd init-operator followed by nxd open.

Quick start

import nxd

# 1. Shield code before any AI call
code = "api_key = 'sk_live_xxxx'"
shielded = nxd.shield(code)
recovered = nxd.unshield(shielded)
print(f"AI sees:  {shielded[:40]}...")
print(f"You see:  {recovered}")
print(f"Match:    {code == recovered}")

# 2. Redact PII before sending to AI
note = "Patient John Smith, DOB 1985-04-12, SSN 432-11-5678"
clean, mapping = nxd.redact(note)
print(f"\nAI gets:  {clean}")
print(f"Original: {nxd.deredact(clean, mapping)}")

# 3. Vault a credential and use it without returning it
vault = nxd.Vault(agent_id="my-agent")
vault.store("stripe_key", "sk_live_xxxx")
result = vault.safe_use(
    "stripe_key",
    lambda key: {"status": "charged", "auth_len": len(key)},
)
print(f"\nAgent got: {result}")
print("Key seen:  never")

# 4. Verify tamper-proof audit chain
nxd.audit.log("session", agent_id="my-agent")
print(f"\nAudit valid: {nxd.audit.verify()}")

Getting Started

  1. pip install nxd
  2. nxd init-operator
  3. nxd open
  4. Use NXD from Python
  5. nxd lock when the session should end

Redaction Levels

redact() returns both the safe text and the local restoration mapping:

import nxd

safe, mapping = nxd.redact("Patient John Smith, SSN 432-11-5678")
restored = nxd.deredact(safe, mapping)
print(safe)
print(restored)

redact() catches common PII and secret formats including emails, phone numbers, SSNs, many API key families, bearer tokens, JWTs, physician names, dates, connection strings, and account numbers.

Important: redact() is pattern-based detection. It reduces exposure, but it is not a guaranteed wall. For complete payload protection, combine it with shield().

redact_strict() is the guaranteed path for opaque transport. It pattern-redacts first, then shields the remaining payload so no plaintext survives in the returned value.

import nxd

shielded, recovery = nxd.redact_strict(
    "John Smith SSN 432-11-5678 sk_live_real_key_here UNUSUAL_FORMAT_12345"
)
restored = nxd.deredact_strict(shielded, recovery)
print("sk_live" not in shielded)
print(restored)

Security Model

Memory Safety

NXD provides SecureString for credential handling that zeroes memory on deletion.

import os
import nxd

vault = nxd.Vault(agent_id="memory-safe")

# Strongest pattern — load from environment and remove immediately
os.environ["API_KEY"] = "sk_live_env_secret_value_2026"
with nxd.SecureString.from_env("API_KEY") as s:
    vault.store("api_key", s.read())

# SecureString — zeroes memory on context exit
with nxd.SecureString("sk_live_example_secret_value_2026") as s:
    vault.store("api_key_copy", s.read())

# secure_store() — zeroes after storing
my_secret = bytearray(b"sk_live_buffered_secret")
vault.secure_store("buffered_key", my_secret)
print(vault.use("api_key_copy", lambda value: len(value)))

Honest caveat: Python string interning means zero-on-delete is still best-effort for short values. SecureString.from_env() is the strongest local pattern because the environment variable is removed immediately after loading.

Callback Safety

Use safe_use() in production to detect accidental credential leakage in callback return values.

import nxd

vault = nxd.Vault(agent_id="callback-safe")
vault.store("stripe_key", "sk_live_xxxx")

class stripe:
    @staticmethod
    def charge(key, amount):
        return {"status": "charged", "amount": amount, "auth_len": len(key)}

# safe_use() raises CredentialLeakError if callback returns credential data
result = vault.safe_use("stripe_key", lambda k: stripe.charge(k, amount=100))
print(result)

# use() when you need callback flexibility
# but callbacks must USE credentials, not RETURN them

Audit Integrity

Audit chain is protected against:

  • Entry modification via HMAC-signed entries
  • Entry injection via sequential hash verification
  • Entry reordering via chained previous-hash checks
  • Entry truncation via a signed manifest with entry count and tail hash

If the manifest is lost, recover with:

import nxd

nxd.audit.log("example", agent_id="recover-demo")
print(nxd.audit.verify())
print(nxd.audit.recover())
print(nxd.audit.verify())

CLI equivalent:

nxd audit-recover

Back up ~/.nxd/ regularly. The encrypted payloads stay there even when the day-to-day unlock secret moves into the OS keychain.

Hosted Key Management

For deployments where local key custody is not acceptable, use the hosted backend:

export VAULT_ADDR="https://vault.example.com"
export VAULT_TOKEN="replace-me"
python3 -c "import nxd; print(type(nxd.Vault(agent_id='prod-agent', hosted=True)).__name__)"

Hosted mode keeps credentials out of ~/.nxd/ and delegates secret custody to HashiCorp Vault. http://127.0.0.1:8200 remains acceptable for local development only and now emits a SecurityWarning.

Import Surface

import nxd

vault = nxd.Vault(agent_id="imports")

# Memory-safe credential handling
with nxd.SecureString("sk_live_example_secret_value_2026") as s:
    vault.store("key", s.read())

credential = bytearray(b"sk_live_buffer")
vault.secure_store("key", credential)    # zeroes after store
vault.safe_use("key", lambda callback_cred: {"status": "ok", "len": len(callback_cred)})  # detects callback leaks
print(nxd.CredentialLeakError.__name__)  # raised on leak detection
print(nxd.PrivacyBudgetExceededError.__name__)
shielded, recovery = nxd.redact_strict("John Smith SSN 432-11-5678")
print(nxd.deredact_strict(shielded, recovery))
import os
os.environ["IMPORT_SURFACE_SECRET"] = "import_surface_secret_value_2026"
with nxd.SecureString.from_env("IMPORT_SURFACE_SECRET") as env_secret:
    print(env_secret.read())

# Audit recovery
print(nxd.audit.recover())               # rebuild manifest
# CLI: nxd audit-recover

Exact-match search

search() is encrypted exact-match lookup over Fernet-protected records. It performs local encrypted scan and returns opaque per-query handles rather than a persisted deterministic token index. This is still not private information retrieval, oblivious RAM, or searchable symmetric encryption.

Channel messaging

channel() is Fernet-based agent messaging with persisted sequence validation. It provides confidentiality and rejects replayed or out-of-order packets across fresh channel instances that share the same local state. It does not provide forward secrecy or asymmetric sender authentication.

Recipient binding

bind() is a derived-key access wrapper. Data is encrypted under a key derived from recipient identity and optional purpose. It is not a formal capability system, a revocable ACL, or a proof-backed authorization framework.

Token substitution

tokenize() replaces sensitive values with encrypted-map-backed tokens. For digit-like identifiers it preserves basic format shape locally; for everything else it emits opaque random tokens. It is useful for local indirection and recovery, not HSM-backed or PCI-style tokenization.

What each primitive actually is

Primitive What it is What it is not
score / match / aggregate FHE via Concrete ML Custom FHE
shield / seal Fernet symmetric encryption New primitive
vault Fernet + PBKDF2 + agent tokens HSM or KMS
sign / verify_signature Ed25519 via cryptography Custom signing
handoff Fernet + replay store + receipts ledger Ratchet protocol
channel Fernet + persisted sequence checks Forward-secret messaging
search Encrypted linear scan + Fernet records PIR or SSE
tokenize Format-aware local token map + Fernet HSM tokenization
bind HKDF-derived key + Fernet Capability system
split Shamir secret sharing Verifiable secret sharing
blur Laplace mechanism Formally certified DP
audit HMAC-backed chain + manifest Append-only ledger
redact Pattern detection + optional shielding Guaranteed semantic wall

Handoff tokens

Handoff tokens are single-use. A token that has already been unpacked raises ReplayError if it is unpacked again.

Replay state is kept in a bounded local seen-token store. Entries older than 30 days are pruned during normal operation. If that seen-token state is lost or corrupted, previously used tokens can become reusable until the store is rebuilt.

import nxd

handoff = nxd.Handoff()
token = handoff.pack({"client": "Jane Doe", "balance": 50000})
payload = handoff.unpack(token)
print(payload)

For multi-agent workflows where the same context is needed by multiple agents, pack a separate token for each agent.

Audit export

import nxd

nxd.audit.log("docs-example", agent_id="my-agent")
nxd.audit.export("audit_report.json")
print("export works")

Benchmarks (MacBook Air, Python 3.11, Concrete ML 1.9.0)

Operation Latency Notes
FHE score (1 record) ~183 ms First-call cold start
FHE score (1k records, parallel) 1.6 s 8 cores, ~1.6 ms/record
FHE match (single pair) 352 ms Cross-system comparison
FHE aggregate (1k records, parallel) 1.8 s ~0.009% quantization error
Credential vault use <1 ms Decrypt in memory only
Proof suite 135/135 passed python3 prove.py
Real-world simulation 34/34 passed python3 realworld.py
Pytest suite 102 passed pytest -q

What NXD does not protect against

NXD protects credentials and sensitive data from AI providers, model context, and ordinary cloud exposure. It does not remove the need for normal endpoint security and key management discipline.

  • Local deployments still inherit host risk. Vault(hosted=True) removes local key custody, but Vault(hosted=False) still stores encrypted key material on the operator machine.
  • split() and blur() are still pending external cryptographic review. They now ship with import-time self-tests, split self-verification, and privacy-budget guards, but that is not a substitute for independent review.
  • search() is local encrypted linear scan. It avoids a persisted equality index, but it is still not PIR, ORAM, or searchable symmetric encryption.
  • channel() uses a long-term key derived from the operator master key. It now persists replay state across fresh channel instances, but it still does not provide forward secrecy.
  • redact() remains best-effort pattern detection. Use redact_strict() when you need guaranteed opaque output.
  • SecureString, secure_store(), and SecureString.from_env() are stronger patterns than raw strings, but CPython memory behavior means zeroing remains best-effort for short secrets.
  • NXD uses FHE for specific compute operations such as score, match, and aggregate. It does not run the full LLM context window under FHE.
  • NXD does not protect against a trusted operator with physical access, because that operator holds the keys by design.
  • Current encryption choices are not presented as quantum-resistant. Post-quantum primitives are not part of the current release.

Operator workflow

Recommended local flow:

nxd init-operator
nxd open

From that point forward, the OS keychain unlocks local NXD use for the session window. NXD_OPERATOR_PASSPHRASE remains available as a legacy fallback and migration path, but it is no longer the primary setup path.

When you use nxd init, NXD can vault .env secrets, replace them with NXD_VAULT::NAME references, and write an encrypted .env.backup.nxd recovery file.

On the MCP path, decrypt-style tools such as nxd_unshield, nxd_unseal_text, and nxd_detokenize no longer return plaintext to the agent. They queue an operator-only reveal:

nxd reveal <reveal_id>

Roadmap

v0.3.x (shipped)

  • ✅ Agent vault isolation
  • ✅ Concurrent vault safety
  • ✅ Passphrase strength enforcement
  • ✅ API key redaction coverage
  • ✅ Audit truncation detection
  • ✅ Uniform error messages (VaultError)
  • SecureString memory safety
  • safe_use() leak detection
  • ✅ Audit manifest recovery
  • ✅ HashiCorp Vault hosted mode
  • redact_strict() guaranteed opaque transport
  • SecureString.from_env() and SecurityWarning
  • split() self-tests and runtime self-verification
  • blur() privacy-budget enforcement and import self-test

v0.4.0 (shipped)

  • Managed backend expansion (AWS KMS, Vault transit patterns, stronger auth flows)
  • Production hosted deployment ergonomics

v0.5.1 (shipped)

  • Keychain service namespace override via NXD_KEYCHAIN_SERVICE for isolated operator-ceremony testing
  • Live macOS keychain ceremony verified separately from the default operator key entry

v0.6.0 (next)

  • Forward secrecy for channel() via explicit X25519 session handshake design
  • Discrete Laplace or snapping mechanism in blur() to avoid floating-point ambiguity
  • External cryptographic audit of split() and blur()

v0.7.0

  • Hardware-accelerated FHE on GPU/TPU
  • Sub-10 ms encrypted inference
  • Stronger search privacy via evaluation of SSE or related schemes

Development

git clone https://github.com/Nexploraai/nxd
cd nxd
pip install -e ".[dev]"
python3 prove.py
pytest -q
python3 realworld.py

License

Proprietary - Nexplora Labs. Free to use in projects, but the source may not be modified, redistributed, resold, or used to build a competing encryption or agent-protection product. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nxd-0.5.1.tar.gz (81.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nxd-0.5.1-py3-none-any.whl (65.3 kB view details)

Uploaded Python 3

File details

Details for the file nxd-0.5.1.tar.gz.

File metadata

  • Download URL: nxd-0.5.1.tar.gz
  • Upload date:
  • Size: 81.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for nxd-0.5.1.tar.gz
Algorithm Hash digest
SHA256 8336a3f41cd6fdf27adb99f11e8545910e07894eea0cf9bc12e303b33e15f83c
MD5 35d5d19fdbc69345861805da6a29bc0a
BLAKE2b-256 fe9d2ccbfba3d987d1634431699bed539bcd8f6e21beabf1140a1aff643a4b75

See more details on using hashes here.

File details

Details for the file nxd-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: nxd-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 65.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for nxd-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8748b1a046f44cab25ff3f3e025a9c85f8afee0878a8b28b560738ef3163ad41
MD5 d7e2141dcb502c7e744735eebd28112d
BLAKE2b-256 254ee764db7c65ff0bcecac9a528bc1e73d044f44c7fd45e6adb1616da3878a8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page