Python client library + CLI for TrueSight DAO's contribution server, Edgar (edgar.truesight.me, source: TrueSightDAO/sentiment_importer).
Every public DAO action — contribution, inventory movement, notarization, QR update, tree planting, proposal vote, sale — is submitted as an RSA-signed event payload to Edgar's POST /dao/submit_contribution endpoint. The browser-side reference implementation lives in TrueSightDAO/dapp (each HTML page under dapp.truesight.me/). This repo is the terminal / script / automation equivalent: same signing, same payload shape, same endpoint — just from Python instead of a browser tab.
- What this repo gives you
- Installation
- Quick start (three commands)
- Onboarding a key —
truesight_dao_client.auth - Submitting signed events —
truesight_dao_client.modules/ - Reading DAO data —
truesight_dao_client.cache/ - AI-agent contributions —
report_ai_agent_contribution - Using the library from your own Python
- Environment variables (
.env) - Architecture — one picture
- Security notes
- Related repos
| Location | Purpose |
|---|---|
truesight_dao_client/edgar_client.py |
Core library. Key generation (RSA-2048, SPKI / PKCS#8 base64 — byte-identical to WebCrypto exports), canonical payload formatting, RSASSA-PKCS1-v1_5 / SHA-256 signing, the multipart POST /dao/submit_contribution, and build_event_cli(...) for zero-boilerplate per-event wrappers. Python port of dapp/scripts/edgar_payload_helper.js. |
truesight_dao_client/auth.py |
OAuth-loopback CLI to onboard this machine's keypair. login signs [EMAIL REGISTERED EVENT] with a 127.0.0.1:<port>/verify callback, spins up a one-shot listener, captures em+vk when the email link is clicked, auto-submits [EMAIL VERIFICATION EVENT]. Fallbacks: verify, status, rotate. Installed as truesight-dao-auth. |
truesight_dao_client/modules/ |
Thin CLI wrappers, one per signed page on dapp.truesight.me/. Named flags for canonical attributes + --attr 'Label=Value' escape hatch + --dry-run. See table. |
truesight_dao_client/cache/ |
Read-side wrappers over the DAO's four public data sources (treasury snapshot, freight lanes, repackaging receipts, contributor voting rights). Each module has a library API and python -m truesight_dao_client.cache.<name> (plus truesight-dao-cache-* console scripts). Swappable backends (GithubRawBackend / GithubContentsBackend / GasBackend) so GAS→GitHub flips are one-liners. |
dapp_digital_signature_onboarding/ |
Operator read-mostly demo mirroring Edgar's Google-Sheets side of the onboarding flow (append VERIFYING row, call the mailer, flip to ACTIVE). Previously lived in TrueSightDAO/tokenomics under python_scripts/examples/. |
requirements.txt |
Mirrors runtime deps from pyproject.toml for pip install -r workflows. |
pyproject.toml |
Installable package truesight-dao-client (import name truesight_dao_client) and console entry points (truesight-dao-auth, per-event CLIs, cache readers). |
.env |
Never committed (0600, gitignored). Holds EMAIL, PUBLIC_KEY (SPKI base64), PRIVATE_KEY (PKCS#8 base64). Written by truesight-dao-auth login (or python -m truesight_dao_client.auth login). Looked up from the current working directory (./.env) unless you pass an explicit path to EdgarClient.from_env(path=...). |
auth.py, edgar_client.py (repo root) |
Thin shims for older scripts; prefer truesight_dao_client imports or the truesight-dao-* commands after pip install. |
From PyPI (once published):
python3 -m venv .venv && source .venv/bin/activate
pip install truesight-dao-clientFrom a git checkout (editable install keeps truesight-dao-* on your PATH):
git clone git@github.com:TrueSightDAO/dao_client.git ~/Applications/dao_client
cd ~/Applications/dao_client
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
# or: pip install -r requirements.txt # library only, no console scriptsPython 3.10+ (uses list[str]-style type hints in several modules).
# 1. Generate a keypair, register it with Edgar, finalise via email click.
truesight-dao-auth login --email you@example.com
# 2. Confirm Edgar agrees you're active.
truesight-dao-auth status
# 3. Emit any signed event — e.g. log 30 min of contribution work.
truesight-dao-report-contribution \
--type "Time (Minutes)" --amount 30 \
--description "Closing out Townhall" \
--contributors "Your Name" --tdg-issued "N/A"Dry-run the third command with --dry-run first if you want to see the signed share text without hitting the wire.
┌─────────────┐ ┌─────────────────────┐
│ your CLI │ [EMAIL REGISTERED │ Edgar (Rails) │
│ auth.py │──────── EVENT]─────▶│ edgar.truesight.me │
│ │ gen_src=127.0.0.1 │ │
│ │ └──────────┬──────────┘
│ │ │
│ │ ┌─────────────────────▼──────────┐
│ │ │ GAS edgar_send_email_verif.gs │
│ │ │ GmailApp.sendEmail(…&vk=…) │
│ │ └─────────────────────┬──────────┘
│ │ │
│ │ (you receive an email) │
│ │ ▼
│ │ ┌──────────────────────────────────────┐
│ ◀────em,vk──┼──┤ browser: http://127.0.0.1:PORT/verify│
│ │ │ ?em=…&vk=… │
│ │ └──────────────────────────────────────┘
│ │ ┌─────────────────────┐
│ │ [EMAIL VERIFICATION│ Edgar writes │
│ │─────── EVENT]──────▶│ Contributors Digital│
│ │ vk + pubkey │ Signatures row: │
│ │ │ Status=ACTIVE, │
│ │ │ col H = now │
│ │ └─────────────────────┘
└─────────────┘
The generation-source URL encoded in the signed payload is the only thing that tells the mailer where to point the link — the auth CLI exploits that by setting it to an ephemeral 127.0.0.1 port bound a moment before the EMAIL REGISTERED EVENT POST. The listener captures em+vk the instant you click, signs [EMAIL VERIFICATION EVENT] with the same keypair, and POSTs it to Edgar. Column H ("Verification Key Consumed", added in sentiment_importer#1024) enforces single-use.
truesight-dao-auth login --email you@example.com # loopback register+verify; default path
truesight-dao-auth verify --vk <value-from-email-url> # manual fallback if you click on a phone
truesight-dao-auth status # GET /dao/check_digital_signature
truesight-dao-auth rotate --email you@example.com # wipe .env keys + generate freshtruesight-dao-auth login is idempotent — if the stored key is already ACTIVE on Edgar it exits with a short message. If the key is mid-verification it says so and points you at verify / rotate.
| Symptom | Cause | Fix |
|---|---|---|
| Gmail shows "preview this link" warning | Link is http://127.0.0.1:… (not HTTPS). |
Click through — it's a local URL. |
| Email link opens but nothing happens in terminal | You clicked on a different device, or the listener timed out (10 min default). | truesight-dao-auth verify --vk <value> (paste the vk= query param from the URL). |
HTTP 422 … No matching pending verification row |
The vk already consumed, or the pub_key doesn't match the row the vk was minted for. | truesight-dao-auth rotate --email you@example.com then log in again. |
HTTP 200 … email_registration.skipped=true |
This browser key is already pending/active — Edgar won't re-send. | Click the previous email, or rotate. |
ReadTimeout on status / contributors |
GAS cold start. | Retry once; GasBackend default is 45 s. |
Every signed page on dapp.truesight.me/ has a matching module under truesight_dao_client/modules/. Each one hardcodes the event name, exposes canonical attributes as named CLI flags (dashes; e.g. --type, --amount), and accepts --attr 'Label=Value' (repeatable) for anything not covered. All modules support --dry-run and --generation-source <URL>.
After pip install -e . or pip install truesight-dao-client, use the truesight-dao-* console commands (see pyproject.toml [project.scripts]). You can also run python -m truesight_dao_client.modules.<name> ….
truesight-dao-report-contribution \
--type "Time (Minutes)" --amount 30 \
--description "Closing out Townhall" \
--contributors "Gary Teh" \
--tdg-issued 50.00
# → HTTP 200, {"status":"success","signature_verification":"success",...}Example when the contribution is USD-denominated under the 1 TDG per 1 USD rule. The DApp maps the USD picker to payload - Type: USD (dapp/report_contribution.html); Amount is the USD number; TDG Issued should match it (e.g. 607.00 TDG for 607.00 USD).
truesight-dao-report-contribution \
--type "USD" \
--amount 607.00 \
--description "Flight tickets for DAO logistics (USD 607.00)." \
--contributors "Gary Teh" \
--tdg-issued 607.00Do not use N/A for TDG Issued when a numeric rubric applies. Tokenomics TDG scoring encodes two headline rules in google_app_scripts/tdg_scoring/grok_scoring_for_telegram_and_whatsapp_logs.gs (checkTdgIssued): 100 TDG per 1 hour of human effort, and 1 TDG per 1 USD of contribution treated as liquidity / dollar outlay. The sheet Intiatives Scoring Rubric summarizes categories (tokenomics/SCHEMA.md — Intiatives Scoring Rubric). Pass --tdg-issued so CLI rows stay consistent with Telegram-scored lines.
Clock time (Time (Minutes), human effort)
Classification analogue: 100TDG For every 1 hour of human effort. Compute TDG as 100 * <minutes> / 60 (e.g. 30 minutes → 50.00 TDG).
USD (dollar amount — liquidity injected / spend the rubric scores per-USD)
Classification analogue: 1TDG For every 1 USD of liquidity injected. Use --type "USD" to mirror the DApp’s USD branch (not Time (Minutes)). Set --amount to the USD number and --tdg-issued to the same numeric total — e.g. 607.00 USD outlay → --tdg-issued 607.00 (Grok example in the same checkTdgIssued string). Proof and context still go in --description (and attachments / destination fields when applicable).
Other types (capital injection events, inventory, AI-agent sessions, …) use the rubric line that applies to that Type / event. The AI-agent contribution CLI defaults Amount / TDG to 0 by convention; see agentic_ai_context/DAO_CLIENT_AI_AGENT_CONTRIBUTIONS.md. Capital and other dedicated events may use truesight-dao-report-capital-injection (or the matching module) instead of truesight-dao-report-contribution when that is the correct Edgar event.
| Module | Console script (examples) | Event tag | Browser equivalent |
|---|---|---|---|
truesight_dao_client/modules/batch_qr_generator.py |
truesight-dao-batch-qr-generator |
[BATCH QR CODE REQUEST] |
batch_qr_generator.html |
truesight_dao_client/modules/create_proposal.py |
truesight-dao-create-proposal |
[PROPOSAL CREATION] |
create_proposal.html |
truesight_dao_client/modules/notarize.py |
truesight-dao-notarize |
[NOTARIZATION EVENT] |
notarize.html |
truesight_dao_client/modules/register_farm.py |
truesight-dao-register-farm |
[FARM REGISTRATION] |
register_farm.html |
truesight_dao_client/modules/repackaging_planner.py |
truesight-dao-repackaging-planner |
[REPACKAGING BATCH EVENT] |
repackaging_planner.html |
truesight_dao_client/modules/report_capital_injection.py |
truesight-dao-report-capital-injection |
[CAPITAL INJECTION EVENT] |
report_capital_injection.html |
truesight_dao_client/modules/report_contribution.py |
truesight-dao-report-contribution |
[CONTRIBUTION EVENT] |
report_contribution.html |
truesight_dao_client/modules/report_ai_agent_contribution.py |
truesight-dao-report-ai-agent-contribution |
[CONTRIBUTION EVENT] (AI agent — PR URLs required) |
Convention: agentic_ai_context/DAO_CLIENT_AI_AGENT_CONTRIBUTIONS.md |
truesight_dao_client/modules/report_dapp_permission_change.py |
truesight-dao-report-dapp-permission-change |
[DAPP PERMISSION CHANGE EVENT] (governor-only — edits permissions.json on treasury-cache) |
Spec: agentic_ai_context/DAPP_PERMISSION_CHANGE_FLOW.md |
truesight_dao_client/modules/report_dao_expenses.py |
truesight-dao-report-dao-expenses |
[DAO Inventory Expense Event] |
report_dao_expenses.html |
truesight_dao_client/modules/report_inventory_movement.py |
truesight-dao-report-inventory-movement |
[INVENTORY MOVEMENT] |
report_inventory_movement.html |
truesight_dao_client/modules/report_sales.py |
truesight-dao-report-sales |
[SALES EVENT] |
report_sales.html |
truesight_dao_client/modules/report_tree_planting.py |
truesight-dao-report-tree-planting |
[TREE PLANTING EVENT] |
report_tree_planting.html |
truesight_dao_client/modules/review_proposal.py |
truesight-dao-review-proposal |
[PROPOSAL VOTE] |
review_proposal.html |
truesight_dao_client/modules/scanner.py |
truesight-dao-scanner |
[QR CODE EVENT] |
scanner.html |
truesight_dao_client/modules/update_qr_code.py |
truesight-dao-update-qr-code |
[QR CODE UPDATE EVENT] |
update_qr_code.html |
truesight_dao_client/modules/withdraw_voting_rights.py |
truesight-dao-withdraw-voting-rights |
[VOTING RIGHTS WITHDRAWAL REQUEST] |
withdraw_voting_rights.html |
Read the browser-equivalent HTML for the canonical attribute list and value-format expectations (dates, coordinates, currency). Pages that don't emit a signed event (read-only dashboards — index.html, stores_nearby.html, stores_by_status.html, store_interaction_history.html, submit_feedback.html, verify_request.html, view_open_proposals.html, restock_recommender.html, shipping_planner.html) aren't mirrored here — they'd need a different client surface (GET helpers).
Four wrappers over the DAO's read-only data sources. Each one has a library API, python -m truesight_dao_client.cache.<name>, and a truesight-dao-cache-* console script.
| Module | Source | CLI example |
|---|---|---|
truesight_dao_client.cache.treasury |
TrueSightDAO/treasury-cache/dao_offchain_treasury.json (GitHub raw) — DAO off-chain inventory snapshot, regenerated on every Telegram-logged inventory movement + safety-net cron. |
truesight-dao-cache-treasury --ledger AGL4 |
truesight_dao_client.cache.freight |
TrueSightDAO/agroverse-freight-audit/pointers/freight_lanes.json (GitHub raw) — freight lane registry. |
truesight-dao-cache-freight --to "San Francisco" |
truesight_dao_client.cache.compositions |
TrueSightDAO/agroverse-inventory/currency-compositions/{uuid}.json — per-UUID repackaging receipts (GitHub raw fetch + GitHub contents API for listing, rate-limited to 60/hr unauthenticated). |
truesight-dao-cache-compositions --list |
truesight_dao_client.cache.contributors |
GAS assetVerify (default) + GitHub-raw dao_members.json (live) — auto-detects shape, either works. See below. |
truesight-dao-cache-contributors · --github · --list · --totals |
The snapshot shipped by tokenomics#237's dao_members_cache_publisher.gs is live now at raw.githubusercontent.com/TrueSightDAO/treasury-cache/main/dao_members.json. Refresh triggers:
sentiment_importer#1028— Edgar enqueuesDaoMembersCacheRefreshWorkeron every successful[EMAIL VERIFICATION EVENT]activation. The worker GETs?action=refresh_dao_members_cache&secret=…on the publisher, which rebuilds the snapshot and commits to the treasury-cache repo.installDaoMembersCacheDailyTrigger()— safety-net daily cron in the GAS. Cache self-heals if a Sidekiq enqueue drops.- Manual operator call:
publishDaoMembersCacheNow()in the Apps Script editor.
Snapshot shape (schema_version: 2, contributor-keyed because one contributor has N simultaneously-active RSA keys — see project_edgar_multiple_active_keys memory):
{
"generated_at": "2026-04-22T…Z",
"schema_version": 2,
"dao_totals": {
"voting_rights_circulated": 2341222.10,
"total_assets": 15086.58,
"asset_per_circulated_voting_right": 0.00644,
"usd_provisions_for_cash_out": 32.42
},
"contributors": [
{ "name": "Gary Teh", "voting_rights": 955414.06,
"public_keys": [{"public_key": "MIIB…", "status": "ACTIVE",
"created_at": "…", "last_active_at": "…"}] }
]
}Contributors.for_public_key(pk) auto-detects which shape the backend returned (GAS ships a single {contributor_name, voting_rights, …} dict; GitHub ships the snapshot above and we scan contributors[*].public_keys[*] locally) and returns an identical-shaped record either way — just with _source: "gas" | "github_cache" appended so callers can tell which path answered. Default remains GAS for zero-surprise migration; flip to GitHub explicitly via Contributors.from_github() or the --github CLI flag, or globally by editing _default_lookup_source in truesight_dao_client/cache/contributors.py.
from truesight_dao_client.cache.treasury import TreasuryCache
tc = TreasuryCache.fetch()
print(tc.totals()) # {item_types, total_units, total_value_usd, ...}
print(tc.manager("Gary Teh")) # one manager's holdings
print(tc.for_ledger("AGL4")) # contents of AGL4
print(tc.item("ceremonial-cacao-500g")) # where a SKU lives
from truesight_dao_client.cache.contributors import Contributors
me = Contributors().for_self() # default GAS backend
print(me["voting_rights"]) # e.g. 955414.06
me_fast = Contributors.from_github().for_self() # fast path — ~50–150ms TTFB vs 2–5s GAS cold start
print(me_fast["_source"], me_fast["_generated_at"])
roster = Contributors.from_github().list_all() # every contributor + their public keys
print(Contributors.from_github().dao_totals()) # DAO-wide aggregates blockEvery cache module delegates reads to a DataSource in truesight_dao_client/cache/_source.py. Three implementations ship:
GithubRawBackend(raw_url)— CDN-fast, auth-free, git-history audit trail. Default for treasury / freight / compositions; opt-in for contributors.GithubContentsBackend(contents_url)— for directory listings thatraw.githubusercontent.comcan't enumerate. Rate-limited to 60 req/hr per IP unauthenticated; surfaces a readable error when throttled.GasBackend(exec_url, params=...)— for GAS web apps. 45 s timeout so cold starts don't spuriously fail. Default fortruesight_dao_client.cache.contributors.
TrueSightDAO/dapp shares the same dao_members.json snapshot via scripts/dao_members_cache.js:
tdg_balance.jsrenders the voting-rights badge from the cache (GAS fallback on miss) — typical 20× latency win vs GAS cold start (dapp#170).create_signature.htmlrenders "Welcome back" optimistically from the cache while Edgar'scheck_digital_signaturecall is in flight (dapp#171).
So a verification landing in Edgar propagates to: Sidekiq worker → GAS publisher → GitHub raw → both the dapp browser badge and any Python scripts using truesight_dao_client.cache.contributors. One snapshot, three consumers.
Special-cased wrapper for [CONTRIBUTION EVENT] submissions that record work done by an AI agent on behalf of a contributor (e.g. Claude Code pairing sessions). Requires at least one https://github.com/TrueSightDAO/.../pull/N URL and an explicit description so the ledger entry is verifiable against a merged PR.
Full convention, format, and review checklist: agentic_ai_context/DAO_CLIENT_AI_AGENT_CONTRIBUTIONS.md.
Every additional DAO event is a three-line call:
from truesight_dao_client import EdgarClient
client = EdgarClient.from_env()
resp = client.submit(
"CONTRIBUTION EVENT",
{
"Type": "Time (Minutes)",
"Amount": "30",
"Description": "Closing out Townhall",
"Contributor(s)": "Gary Teh",
},
)
print(resp.status_code, resp.text)Or sign without sending (handy for testing or for pasting into a manual workflow):
payload, request_txn_id, share_text = client.sign(
"CONTRIBUTION EVENT",
{"Type": "Time (Minutes)", "Amount": "30", "Description": "…", "Contributor(s)": "…"},
)
print(share_text)The attribute dict matches the - Label: value lines emitted by the browser-side edgar_payload_helper.js + each dapp/*.html, so read that file as the canonical event contract.
| Key | Purpose | Set by |
|---|---|---|
EMAIL |
The email address this identity registered with (used for EMAIL REGISTERED + EMAIL VERIFICATION events). |
truesight-dao-auth login / rotate |
PUBLIC_KEY |
RSA-2048 SPKI base64, no PEM headers. Byte-identical to crypto.subtle.exportKey('spki', ...) + btoa. |
truesight-dao-auth login / rotate |
PRIVATE_KEY |
RSA-2048 PKCS#8 base64, no PEM headers, unencrypted. Keep this file mode 0600. |
truesight-dao-auth login / rotate |
The .gitignore covers .env, .env.*, and an exception for an optional .env.example. If you ever commit a private key by mistake, rotate immediately (truesight-dao-auth rotate) — multiple active keys per contributor are fine; old leaked keys can be marked inactive in the sheet by an operator.
┌───────────────────────────────────────────────────────────────────────┐
│ your shell / script │
│ │
│ auth.py modules/*.py cache/*.py │
│ │ │ │ │
│ └─────signed POST───────┘ │ │
│ │ │ │
│ ▼ ▼ │
│ edgar_client.EdgarClient cache/_source.py │
│ (RSA-2048, PKCS1v15/SHA256, ┌──────────┬───────────┐ │
│ SPKI pub / PKCS8 priv) │ Github │ Gas │ │
│ │ │ Raw / │ Backend │ │
│ │ │ Contents │ │ │
│ ▼ └────┬─────┴─────┬─────┘ │
│ POST https://edgar.truesight.me/ │ │ │
│ dao/submit_contribution │ │ │
│ │ │ │ │
└────────────────┼─────────────────────────────────┼───────────┼────────┘
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌───────────────┐ ┌───────────┐
│ sentiment_ │ │ TrueSightDAO/ │ │ script. │
│ importer (Rails│ │ treasury-cache│ │ google. │
│ on EC2) │ │ /… JSON files │ │ com/macros│
│ — Edgar │ │ (GitHub raw) │ │ /s/…/exec │
└────────────────┘ └───────────────┘ └───────────┘
│
▼
Google Sheet tabs:
Contributors Digital Signatures,
Contributors voting weight,
TDG ledger, etc.
- Key storage.
PRIVATE_KEYlives in.envmode 0600, gitignored. Don't check it in. If a key leaks,truesight-dao-auth rotate— Edgar allows multiple active keys so revoking an old one is an operator action on the sheet, not a hard lockout. - Loopback URLs. The email verification link is
http://127.0.0.1:<port>/verify?em=…&vk=…. Anyone with network access to your loopback interface during the 10 min window could hit it; in practice that's you + anything else running on your laptop. Not a concern on a single-user machine. - Single-use verification keys. Column H on Contributors Digital Signatures enforces single-use per (
sentiment_importer#1024): a second click with the same vk returnsalready_consumed: trueif the same public key is signing, or a hard reject if a different key is. - No secrets in signed payloads. Everything in the signed share text is world-readable (Edgar logs it to the Telegram raw-logs sheet). Don't put API keys, sensitive notes, or internal URLs in attribute values.
- GitHub cache. The forthcoming
dao_members.jsonmust publish only governance-public fields (name, public keys, voting weight). Emails fromContributors contact information(column D) are onboarding data and must not leak — the publisher script projects columns explicitly.
TrueSightDAO/dapp— browser-side reference implementation; one HTML page per signed event.TrueSightDAO/sentiment_importer— Edgar itself (Rails). Signature verify + sheet write logic:app/services/dao_email_registration_service.rb,app/models/gdrive/contributors_digital_signatures.rb.TrueSightDAO/tokenomics— canonicalSCHEMA.md+ Apps Script projects (tdg_identity_management,tdg_inventory_management, etc.) that maintain the Google Sheets ledger and publish the GitHub JSON caches.TrueSightDAO/treasury-cache— pre-computed JSON snapshot of DAO off-chain treasury. Consumed bycache.treasury.TrueSightDAO/agroverse-freight-audit— freight lane registry consumed bycache.freightanddapp/shipping_planner.html.TrueSightDAO/agroverse-inventory— per-request repackaging receipts consumed bycache.compositionsanddapp/repackaging_planner.html.TrueSightDAO/agentic_ai_context— AI-agent operating conventions, includingDAO_CLIENT_AI_AGENT_CONTRIBUTIONS.mdandPROJECT_INDEX.md.