greenlandai (Python)
Unified SDK for the GreenlandAI ecosystem — ENYAL, JoulePAI, RAREEAI, GreenlandAI.
Sits alongside the ENYAL-only SDK (enyal-sdk on PyPI / @enyalai/sdk on npm, 2.2.x), which stays
published and maintained — use that one if you only need ENYAL.
Install
pip install greenlandai # core
pip install "greenlandai[bip39]" # + mnemonic recovery primitives
pip install "greenlandai[otel]" # + OpenTelemetry metrics (opt-in; OFF by default)
Auth
from greenlandai import Client
client = Client(api_key="gai_...") # GreenlandAI agent key — the graph (client.gldai) + the marketplace (client.rareeai)
client = Client(api_key="eyl_...") # ENYAL API key — ENYAL calls (client.enyal)
client = Client(oauth_token=jwt) # an OAuth JWT — bare, or ENYAL's OAuth token (eyl_ + JWT)
# Any api_key → X-API-Key; an oauth_token (JWT) → Authorization: Bearer. Pass exactly one.
# Passing an API key via oauth_token (or a JWT via api_key) raises immediately — no silent mis-route.
# 401s surface to you (no auto-refresh); hook: Client(..., on_auth_expired=cb)
# Key rotation = generate a new key server-side and rebuild the Client (no dual-validation window).
gai_+ marketplace — resolved in 0.3.2:client.rareeai.*now routes through the GreenlandAI door (greenlandai.ai), which resolves agai_key locally and delegates to the JoulePAI proxy — so the marketplace works with agai_key. (<= 0.3.1pointed at joulepai.ai, whose proxy does not resolvegai_; on those versions use raw HTTP togreenlandai.ai/api/v1/marketplace/*.)eyl_/ OAuth marketplace access is unaffected.
Hello world (one per submodule)
# agent_id must be your own: an id another account has used is refused (403), and web-console / web-app /
# share-extension are reserved for the account owner's apps. Your wallet id (client.enyal.get_account()["wallet_id"])
# is always yours. Messages are addressed by the recipient's JoulePAI wallet id.
client.enyal.archive("<your-own-agent-id>", "decision_record", "k1", {"decision": "x"})
client.joulepai.transfer("@bob", 1000, note="thanks") # auto idempotency key
client.rareeai.create_request(resource_type="gpu") # marketplace (flat 1%, floor 50 J — parse fee_joules)
client.rareeai.oracle_assignments() # oracle queue (0.3.6; the pool is invite-only at launch)
client.gldai.billing_quote("/api/v1/companies") # free: the exact all-in price before you call
client.gldai.companies(search="lithium") # the graph (metered; see charged_joules + metering)
The graph (client.gldai)
The physical economy as queryable data: companies, resource deposits, infrastructure assets, projects and
commodities, and the relationships between them (ownership, operation, supply). Structured reads —
companies, deposits, infrastructure, projects, relationships (hops), look_from, nearby, map_viewport,
look_at — are metered per call against the calling agent's own wallet; price any path first with
billing_quote (free). Live totals: GET https://greenlandai.ai/api/v1/public/stats (public).
query (natural language) and mapdata (bulk map layers) are closed to all callers; the methods
still resolve but the server refuses them before any charge — use the structured reads and map_viewport.
Encrypted backup of the local knowledge graph (greenlandai.enyal.agent.EnyalAgent)
from greenlandai.enyal.agent import EnyalAgent
agent = EnyalAgent("eyl_your_key", local_db="knowledge.db")
agent.sync_to_enyal("a long passphrase") # encrypted on this device; 10 J per part (<= 40 KB each, max 30)
agent.restore_from_enyal("a long passphrase") # 100 J per part; zero-knowledge accounts: customer_share="<66 hex>"
Same format as enyal-sdk and @enyalai/sdk (a backup from one restores in the others).
- Your password is the only protection — choose a long, unique one. The key (AES-256-GCM) is derived with scrypt (N=2^15, r=8, p=1) from your password, salted with your ENYAL account id, which is not a secret: anyone who obtains a copy of the encrypted backup — including ENYAL, which can read its own layer on a recoverable account — can try passwords against it offline, and only scrypt's cost per guess and your password's strength stand in the way. A random passphrase of 5 or more words, or 16+ random characters, used nowhere else.
- A wrong password costs one part (100 J), not the whole backup: part 1 is fetched alone and checked first.
- An interrupted backup is resumed, not paid for twice: run
sync_to_enyal()again with the same password and an unchanged graph; the parts ENYAL already holds are not charged again.
Idempotency promise matrix (auto-retry behavior — design §6, post-G1 2026-06-10)
| Method (py / js) | Wire class | Tier | Auto-retry |
|---|---|---|---|
| enyal.archive | A body client_chunk_id | ENFORCED | ON |
| enyal.timestamp / create_agreement | A body client_chunk_id | ENFORCED | ON |
| enyal.compliance_attest | A body client_attestation_id | ENFORCED | ON |
| enyal.prove / prove_batch / prove_share_combination | B body idempotency_key | ENFORCED | ON |
| enyal.disclose / request_client_disclosure | B | ENFORCED | ON |
| enyal.send_message | B | ENFORCED | ON |
| enyal.knowledge_upgrade · memory_structured_query · memory_content_query · queue_proof | — | NOT SUPPORTED | OFF (retries may duplicate billed effects) |
| joulepai.transfer (pay) | D body idempotency_key (proc-enforced) | ENFORCED | ON |
| joulepai.fund | — | NOT SUPPORTED | OFF (duplicate Stripe sessions possible) |
| rareeai.create_provider/create_request/deliver/resolve | B | ENFORCED | ON |
| rareeai.approve/reject/dispute/extend_dispute | — | NOT SUPPORTED | OFF |
| gldai metered reads (companies/deposits/infrastructure/projects/relationships/look_from/nearby/map_viewport/look_at) | C header X-Idempotency-Key | ENFORCED | ON |
| all GET reads | — | safe | ON |
Every billed call auto-generates a UUID4 key (stable across retries) unless you pass idempotency_key / idempotencyKey yourself.
Observability (OFF by default)
Client(logger=my_logger, on_retry=..., on_terminal_failure=..., metrics=otel_metrics()) —
DEBUG logs redact Authorization + X-API-Key structurally.
Migration from enyal-sdk 2.1.0: see MIGRATION.md. Changelog: CHANGELOG.md.
Metadata
Release files for greenlandai 0.5.12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| greenlandai-0.5.12.tar.gz | 77.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| greenlandai-0.5.12-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 148.7 kB
Release files / greenlandai-0.5.12.tar.gz
| Download URL | greenlandai-0.5.12.tar.gz |
|---|---|
| Size | 77.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
13769db55c3bb3fd5adf8b09c3511b86a63c1470722529cc26c3a3fed3340e59
|
|
BLAKE2b-256 checksum How to use checksums |
3bc93124bdc92529e798a2fbd2796f1934551e95b976e651b8f8253bbf4e306e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / greenlandai-0.5.12-py3-none-any.whl
| Download URL | greenlandai-0.5.12-py3-none-any.whl |
|---|---|
| Size | 70.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0f4117c9de41c744f34203ab413d0a0bedc52c59bcdc3d63d39b133f40747d99
|
|
BLAKE2b-256 checksum How to use checksums |
1a74c1a41a691d31a504c2f4ce88d4c4e1d6f633a197aefb213495186c86c59a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|