Overview
@grantex/gemma (TypeScript) and grantex-gemma (Python) provide offline authorization for Gemma 4 on-device agents. The SDK handles consent bundle creation, JWT verification without network calls, scope enforcement, tamper-evident audit logging, and cloud sync.
Installation
createConsentBundle
Create a consent bundle from the Grantex API. This is the only call that requires network connectivity.Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Grantex developer API key |
baseUrl | string | No | API base URL (default https://api.grantex.dev) |
agentId | string | Yes | Agent ID requesting the bundle |
userId | string | Yes | End-user / principal ID granting consent |
scopes | string[] | Yes | Scopes the agent is requesting |
offlineTTL | string | No | Offline validity duration (default '72h') |
offlineAuditKeyAlgorithm | string | No | Audit signing key algorithm (default 'Ed25519') |
storage | string | No | 'encrypted-file', 'keychain', or 'secure-enclave' |
storagePath | string | No | File path when storage is 'encrypted-file' |
Returns
Promise<ConsentBundle> — see ConsentBundle type below.
Example
Errors
| HTTP Status | Code | Cause |
|---|---|---|
| 401 | UNAUTHORIZED | Invalid API key |
| 404 | AGENT_NOT_FOUND | Agent ID does not exist |
| 422 | INVALID_SCOPES | One or more scopes are not registered |
createOfflineVerifier
Create an offline JWT verifier using a pre-fetched JWKS snapshot. No network calls are made during verification.Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
jwksSnapshot | JWKSSnapshot | Yes | Pre-fetched JWKS keys from the consent bundle |
clockSkewSeconds | number | No | Allowed clock-skew tolerance (default 30) |
requireScopes | string[] | No | Scopes that must be present in every token |
maxDelegationDepth | number | No | Maximum delegation depth allowed (inclusive) |
onScopeViolation | 'throw' | 'log' | No | Behaviour on scope failure (default 'throw') |
Returns
OfflineVerifier — an object with a single verify(token: string) method.
OfflineVerifier.verify(token)
Verifies a Grantex grant token offline. Returns aVerifiedGrant on success.
VerifiedGrant fields:
| Field | Type | Description |
|---|---|---|
agentDID | string | Agent DID from the agt claim |
principalDID | string | Principal ID from the sub claim |
scopes | string[] | Scopes from the scp claim |
expiresAt | Date | Token expiry |
jti | string | Token ID |
grantId | string | Grant ID (from grnt claim, falls back to jti) |
depth | number | Delegation depth (0 = root grant) |
Example
Errors
| Error Class | Code | Cause |
|---|---|---|
OfflineVerificationError | MALFORMED_TOKEN | JWT cannot be decoded |
OfflineVerificationError | BLOCKED_ALGORITHM | none or HS256 algorithm used |
OfflineVerificationError | MISSING_KID | JWT header has no kid |
OfflineVerificationError | KID_NOT_FOUND | No matching key in JWKS snapshot |
OfflineVerificationError | VERIFICATION_FAILED | Signature invalid |
OfflineVerificationError | FUTURE_IAT | iat claim is in the future |
OfflineVerificationError | DELEGATION_DEPTH_EXCEEDED | Depth exceeds max |
TokenExpiredError | TOKEN_EXPIRED | Token has expired |
ScopeViolationError | SCOPE_VIOLATION | Required scopes missing |
createOfflineAuditLog
Create an append-only, Ed25519-signed, SHA-256 hash-chained audit log backed by a JSONL file.Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
signingKey | { publicKey, privateKey, algorithm } | Yes | Ed25519 key pair from the consent bundle |
logPath | string | Yes | Path to the JSONL log file |
maxSizeMB | number | No | Maximum file size before rotation (default 50) |
rotateOnSize | boolean | No | Whether to auto-rotate (default true) |
Returns
OfflineAuditLog with methods:
| Method | Signature | Description |
|---|---|---|
append | (entry: AuditEntry) => Promise<SignedAuditEntry> | Append a signed entry |
entries | () => Promise<SignedAuditEntry[]> | Read all entries |
unsyncedCount | () => Promise<number> | Count of un-synced entries |
markSynced | (upToSeq: number) => Promise<void> | Mark entries as synced |
AuditEntry fields
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Action name (e.g. 'calendar.read') |
agentDID | string | Yes | Agent DID that performed the action |
grantId | string | Yes | Grant ID authorizing the action |
scopes | string[] | Yes | Scopes on the grant |
result | string | Yes | Outcome ('success', 'auth_failure', etc.) |
metadata | Record<string, unknown> | No | Arbitrary metadata |
Example
storeBundle / loadBundle
Encrypt aConsentBundle to disk with AES-256-GCM, or decrypt it back.
storeBundle
| Parameter | Type | Description |
|---|---|---|
bundle | ConsentBundle | The bundle to encrypt |
path | string | File path to write |
encryptionKey | string | Passphrase (hashed via SHA-256 to derive AES key) |
Promise<void>.
loadBundle
| Parameter | Type | Description |
|---|---|---|
path | string | File path to read |
encryptionKey | string | Passphrase used during storeBundle |
Promise<ConsentBundle>.
Throws BundleTamperedError if decryption or integrity check fails.
File format
Example
refreshBundle / shouldRefresh
Manage consent bundle lifecycle.shouldRefresh returns true when the bundle has less than 20% of its TTL remaining. refreshBundle calls the Grantex API to get a fresh bundle.
shouldRefresh
| Parameter | Type | Description |
|---|---|---|
bundle | ConsentBundle | The bundle to check |
boolean.
refreshBundle
| Parameter | Type | Description |
|---|---|---|
bundle | ConsentBundle | The bundle to refresh |
apiKey | string | Developer API key |
baseUrl | string | API base URL (default https://api.grantex.dev) |
Promise<ConsentBundle> — a fresh bundle with extended offlineExpiresAt, new JWKS snapshot, and rotated audit keys.
Example
enforceScopes / hasScope
Utility functions for scope checking.enforceScopes
ThrowsScopeViolationError if any of requiredScopes is missing from grantScopes.
hasScope
Returnsboolean — whether a specific scope is present.
computeEntryHash / verifyChain
Audit log integrity utilities.computeEntryHash
Compute the SHA-256 hash of an audit entry. Hash input format:verifyChain
Verify the integrity of an ordered sequence ofSignedAuditEntry objects. Checks:
- Each entry’s
hashmatches the recomputed value - Each entry’s
prevHashmatches the previous entry’shash(first entry must useGENESIS_HASH) - Sequence numbers are consecutive
{ valid: true } or { valid: false, brokenAt: number }.
Example
syncAuditLog
Sync un-synced audit log entries to the Grantex cloud in batches with exponential back-off retry.Parameters (SyncOptions)
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | Grantex API URL |
apiKey | string | Yes | Developer API key |
bundleId | string | Yes | Consent bundle ID linking entries to this session |
batchSize | number | No | Entries per batch (default 100) |
Returns
SyncResult:
| Field | Type | Description |
|---|---|---|
syncedCount | number | Total entries synced |
hasErrors | boolean | Whether any batch failed |
errors | string[] | Error messages for failed batches |
withGrantexAuth (Google ADK)
Wrap a Google ADKFunctionTool with offline Grantex authorization. Before the tool executes, the grant token is verified and scopes are enforced. After execution, an audit entry is logged.
Parameters (GoogleADKAuthOptions)
| Parameter | Type | Description |
|---|---|---|
verifier | OfflineVerifier | Offline verifier instance |
auditLog | OfflineAuditLog | Audit log for recording actions |
requiredScopes | string[] | Scopes required to invoke the tool |
grantToken | string | Grant token JWT |
withGrantexAuth (LangChain)
Wrap a LangChainStructuredTool with offline Grantex authorization. Same behavior as the ADK adapter but wraps _call or invoke methods.
Parameters (LangChainAuthOptions)
| Parameter | Type | Description |
|---|---|---|
verifier | OfflineVerifier | Offline verifier instance |
auditLog | OfflineAuditLog | Audit log for recording actions |
requiredScopes | string[] | Scopes required to invoke the tool |
grantToken | string | Grant token JWT |
ConsentBundle Type
Error Classes
| Class | Code | Description |
|---|---|---|
GrantexAuthError | varies | Base error for all auth failures |
OfflineVerificationError | MALFORMED_TOKEN, BLOCKED_ALGORITHM, MISSING_KID, KID_NOT_FOUND, VERIFICATION_FAILED, FUTURE_IAT, DELEGATION_DEPTH_EXCEEDED | JWT verification failures |
TokenExpiredError | TOKEN_EXPIRED | Token has expired beyond clock-skew window |
ScopeViolationError | SCOPE_VIOLATION | Required scopes missing from grant |
BundleTamperedError | BUNDLE_TAMPERED | Bundle file decryption or integrity check failed |
HashChainError | HASH_CHAIN_BROKEN | Audit log hash chain integrity failure |
GrantexAuthError, which extends Error and includes a code property.