Skip to content

Offline Mode

Offline mode provides full cryptographic attestation without requiring an API key. Perfect for development, testing, and air-gapped environments.

import os
from glacis import Glacis
# No API key needed — just a 32-byte signing seed
glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
receipt = glacis.attest(
service_id="local-dev",
operation_type="inference",
input={"prompt": "What is 2+2?"},
output={"response": "4"},
)
print(f"Receipt ID: {receipt.id}") # oatt_xxx (offline attestation)
print(f"Witness status: {receipt.witness_status}") # "UNVERIFIED"
print(f"Signature: {receipt.signature[:32]}...") # Ed25519 signature

In offline mode, the SDK:

  1. Generates a local Ed25519 keypair from the signing_seed you provide
  2. Hashes payloads using SHA-256 with RFC 8785 canonical JSON
  3. Signs receipts locally with the Ed25519 private key
  4. Stores receipts in a local SQLite database at ~/.glacis/glacis.db
FeatureOfflineOnline
API key requiredNoYes
Signing seed requiredYes (32 bytes)No
SigningLocal Ed25519Glacis witness network
Merkle proofsNoYes
Transparency logNoYes
Verification URLNoYes
Witness status"UNVERIFIED""WITNESSED"

The signing_seed parameter is required for offline mode and must be exactly 32 bytes. You can generate one randomly or derive it deterministically for testing:

import os
from glacis import Glacis
# Provide a cryptographically secure random seed (recommended for production)
seed = os.urandom(32)
glacis = Glacis(mode="offline", signing_seed=seed)
# Or derive from a passphrase (for testing only!)
import hashlib
seed = hashlib.sha256(b"my-test-seed").digest()
glacis = Glacis(mode="offline", signing_seed=seed)

For production use, generate the seed once and store it in a secrets manager or environment variable. Reuse the same seed across sessions so you can always verify previously signed receipts.

# Generate once, store securely:
import os
print(os.urandom(32).hex())

Then load it at runtime:

import os
from glacis import Glacis
# Production: load seed from environment or secrets manager
seed = bytes.fromhex(os.environ["GLACIS_SIGNING_SEED"])
glacis = Glacis(mode="offline", signing_seed=seed)

Offline receipts are stored in SQLite at ~/.glacis/glacis.db. You can retrieve the most recent receipt using get_last_receipt():

import os
from glacis import Glacis
glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
# Create a receipt
receipt = glacis.attest(
service_id="local-dev",
operation_type="inference",
input={"prompt": "Hello"},
output={"response": "Hi!"},
)
# Retrieve the last receipt
last = glacis.get_last_receipt()
if last:
print(f"{last.id}: {last.timestamp}")

For more storage options including JSONL and custom paths, see Storage Backends.

import os
from glacis import Glacis
glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
# Create a receipt
receipt = glacis.attest(
service_id="test",
operation_type="inference",
input={"prompt": "test"},
output={"response": "result"},
)
# Verify the signature
result = glacis.verify(receipt)
print(f"Signature valid: {result.signature_valid}") # True
print(f"Overall valid: {result.valid}") # True

The verify() method returns an OfflineVerifyResult with these fields:

FieldTypeDescription
validboolWhether the attestation is valid overall
witness_statusstrAlways "UNVERIFIED" for offline receipts
signature_validboolWhether the Ed25519 signature is valid
attestationAttestation | NoneThe verified attestation object
errorstr | NoneError message if verification failed

Verify offline receipts from the command line:

Terminal window
# Save receipt to file
python -c "
import os, json
from glacis import Glacis
glacis = Glacis(mode='offline', signing_seed=os.urandom(32))
receipt = glacis.attest(
service_id='test',
operation_type='test',
input={'test': True},
output={'ok': True}
)
print(json.dumps(receipt.model_dump(), indent=2))
" > receipt.json
# Verify
python -m glacis verify receipt.json

Output:

Receipt: oatt_abc123...
Type: Offline
Status: VALID
Signature: PASS
ScenarioRecommendation
Local developmentOffline mode
CI/CD testingOffline mode
Air-gapped environmentsOffline mode
Internal audits onlyOffline mode (acceptable)
External auditsOnline mode (recommended)
Customer due diligenceOnline mode (required)
Published researchOnline mode (recommended)

When you need third-party verifiability:

import os
from glacis import Glacis
# Before: offline
glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
# After: online
glacis = Glacis(api_key="glsk_live_...")

That’s it. The attest() and verify() API is identical — only the witness status changes from "UNVERIFIED" to "WITNESSED".