Skip to main content

What You’ll Build

This quickstart walks you through the full offline authorization lifecycle for a Gemma 4 on-device agent:
  1. Create a consent bundle while online (one API call)
  2. Verify grant tokens offline using the JWKS snapshot in the bundle
  3. Log actions to a tamper-evident audit log (hash-chained, Ed25519-signed)
  4. Sync the audit log back to the Grantex cloud when connectivity returns
The entire verification path runs locally with zero network calls.

Prerequisites

  • Node.js 18+ (TypeScript) or Python 3.10+ (Python)
  • A Grantex developer account and API key (sign up free)
  • A registered agent with the scopes your Gemma 4 model needs

Step 1: Install

npm install @grantex/gemma
The consent bundle packages everything the device needs to operate offline: a signed grant token, a JWKS snapshot for signature verification, and an Ed25519 key pair for audit signing.
import {
  createConsentBundle,
  storeBundle,
} from '@grantex/gemma';

const bundle = await createConsentBundle({
  apiKey: process.env.GRANTEX_API_KEY!,
  agentId: 'ag_01HXYZ...',
  userId: 'user_abc123',
  scopes: ['calendar:read', 'email:send'],
  offlineTTL: '72h',
});

// Encrypt and persist to disk for offline use
await storeBundle(bundle, './consent-bundle.enc', process.env.BUNDLE_KEY!);

console.log('Bundle created:', bundle.bundleId);
console.log('Offline until:', bundle.offlineExpiresAt);
This is the only step that requires network connectivity. Everything after this runs entirely on-device.

Step 3: Create an Offline Verifier

Load the bundle and create a verifier that uses the embedded JWKS snapshot.
import {
  loadBundle,
  createOfflineVerifier,
} from '@grantex/gemma';

const bundle = await loadBundle('./consent-bundle.enc', process.env.BUNDLE_KEY!);

const verifier = createOfflineVerifier({
  jwksSnapshot: bundle.jwksSnapshot,
  requireScopes: ['calendar:read'],
  clockSkewSeconds: 30,
});

Step 4: Verify a Token Offline

Every time the Gemma 4 agent attempts an action, verify the grant token before allowing it. No network call is made.
try {
  const grant = await verifier.verify(bundle.grantToken);

  console.log('Agent DID:', grant.agentDID);
  console.log('Principal:', grant.principalDID);
  console.log('Scopes:', grant.scopes.join(', '));
  console.log('Expires:', grant.expiresAt.toISOString());
} catch (err) {
  console.error('Verification failed:', err);
}

Step 5: Create an Offline Audit Log

Every authorized action should be recorded. The audit log is append-only, Ed25519-signed, and hash-chained so tampering is detectable at sync time.
import { createOfflineAuditLog } from '@grantex/gemma';

const auditLog = createOfflineAuditLog({
  signingKey: bundle.offlineAuditKey,
  logPath: './audit-log.jsonl',
});

// Log an action
const entry = await auditLog.append({
  action: 'calendar.read',
  agentDID: grant.agentDID,
  grantId: grant.grantId,
  scopes: grant.scopes,
  result: 'success',
  metadata: { eventCount: 12 },
});

console.log(`Audit entry #${entry.seq} — hash: ${entry.hash.slice(0, 12)}...`);

Step 6: Sync When Back Online

When connectivity returns, upload the offline audit entries to the Grantex cloud. The server verifies the hash chain and Ed25519 signatures, then reconciles the entries with the cloud audit log.
import { syncAuditLog } from '@grantex/gemma';

const result = await syncAuditLog(auditLog, {
  endpoint: 'https://api.grantex.dev',
  apiKey: process.env.GRANTEX_API_KEY!,
  bundleId: bundle.bundleId,
  batchSize: 100,
});

console.log(`Synced ${result.syncedCount} entries`);
if (result.hasErrors) {
  console.warn('Sync errors:', result.errors);
}

Expected Output

Bundle created: cb_01HXYZ...
Offline until: 2026-04-06T12:00:00.000Z
Agent DID: did:grantex:ag_01HXYZ...
Principal: user_abc123
Scopes: calendar:read, email:send
Expires: 2026-04-04T00:00:00.000Z
Audit entry #1 — hash: a3f29c81b4e2...
Synced 1 entries

Environment Variables

VariableDefaultDescription
GRANTEX_API_KEYDeveloper API key from the Grantex portal
BUNDLE_KEYPassphrase for AES-256-GCM bundle encryption

Next Steps