Skip to content

Add local-first Python and Kotlin SDKs wrapping atomic_lib - #1277

Draft
joepio wants to merge 11 commits into
developfrom
cursor/python-sdk-716d
Draft

Add local-first Python and Kotlin SDKs wrapping atomic_lib#1277
joepio wants to merge 11 commits into
developfrom
cursor/python-sdk-716d

Conversation

@joepio

@joepio joepio commented Aug 16, 2026

Copy link
Copy Markdown
Member

Related Issues

Adds a Python SDK (python/, import atomic_data) and a Kotlin / UniFFI SDK (ffi/, package dev.atomicdata). No issue number.

Rebased onto latest develop.

Checklist

  • Add changelog entry linking to issue, describe API changes
  • Add or update tests if needed
  • Update docs if needed

What

Local-first SDKs that wrap atomic_lib — the same pattern as the WASM (wasm-bindgen) and Flutter (flutter_rust_bridge) bindings. Not HTTP-only clients: the default is a local redb store plus Iroh P2P, and the same HTTP paths atomic_lib already has.

  • Schema / external resources: get("https://…") GETs JSON-AD and caches it. That is how unknown Class / Property URLs are validated. The core ontology (Folder, PlainText, name, …) is bundled, so those work offline.
  • server= origin: AtomicServer /search and save_remote() / saveRemote() (POST /commit). did:ad: remote saves need this origin; https:// subjects post to their own host.

A server is not required for local CRUD or Iroh sync. Writes are signed Loro commits. Two devices converge over Iroh the same way Flutter does.

Python (python/, PyO3 + maturin)

  • Store.open(path, server=None) / Store.in_memory(server=None) — redb on disk or in memory
  • setup, load_agent, create / get / search / query / delete, flush
  • dict-like Resource with .save() / .save_remote() / .destroy()
  • Iroh P2P: start_peer(), announce(), sync_with(node_id), wait_for(), live push on save
  • atomic_data.urls for well-known class and property URLs
  • Install: cd python && uv sync && uv run pytest -q. From source this compiles Rust. On Windows that needs Visual Studio Build Tools (MSVC link.exe); GNU/MinGW is not supported. GitHub Actions Python SDK runs the same command on ubuntu-latest and windows-latest.

Kotlin (ffi/, UniFFI)

Same product surface, generated for the JVM (dev.atomicdata):

  • Store.open(path, server) / Store.inMemory(server)
  • setup, loadAgent, create / get / search / query / delete, flush
  • Resource with set / get / save / saveRemote / destroyResource / toJson
  • Iroh P2P: startPeer(), announce(), syncWith(nodeId), waitFor(), live push on save
  • Urls constants matching Python

destroyResource() is not named destroy() because UniFFI already uses that for FFI handle teardown.

start_peer / startPeer is process-global (one Iroh Router / NodeID per OS process). Two nodes = two processes.

Both crates are excluded from the Cargo workspace (same reason as flutter/rust) so workspace clippy/nextest do not compile PyO3 / UniFFI / Iroh on every Rust CI run.

Python stays on PyO3. Kotlin is the UniFFI entry; Swift and the Android Binder host can generate from ffi/ later. Android AAR / cargo-ndk is not in this PR.

Test

  • Python: 22 pytest cases via uv run pytest (HTTP schema get(), search() without a server, save_remote() error path, two-OS-process Iroh sync)
  • UniFFI crate: 17 cargo test cases in ffi/
  • Kotlin JVM: 18 JUnit cases (./gradlew test), including HTTP schema get() and a two-OS-process Iroh sync
Open in Web Open in Cursor 

@cursor cursor Bot changed the title Add a local-first Python SDK wrapping atomic_lib via PyO3 Add local-first Python and Kotlin SDKs wrapping atomic_lib Aug 16, 2026
@cursor
cursor Bot force-pushed the cursor/python-sdk-716d branch from 945611c to efde1d6 Compare August 18, 2026 15:30
@joepio

joepio commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

I asked @ANONYMOUSZED-beep to check out this, since they opened #1276 (python docs)

@ANONYMOUSZED-beep

Copy link
Copy Markdown

Thanks for the ping. I checked out PR #1277 locally and reviewed the Python SDK branch.

The PR looks reasonable from inspection, and the GitHub CI status is green on the current head commit. I tried running the Python SDK tests locally with uv run pytest -q, but my Windows environment failed during the maturin/Rust extension build because the MSVC C++ build tools/linker are not available, so I could not independently verify the full test suite locally.

No obvious blocker from my review. The only caveat is that my local verification was limited by host build tooling rather than a failure in the PR itself.

@cursor
cursor Bot force-pushed the cursor/python-sdk-716d branch from efde1d6 to d16da91 Compare August 19, 2026 13:50
@cursor

cursor Bot commented Aug 19, 2026

Copy link
Copy Markdown

@ANONYMOUSZED-beep thanks for checking it out.

The uv run pytest failure on your machine is the Windows linker, not the SDK. From source this package compiles a PyO3 extension over atomic_lib, and on Windows that needs MSVC’s link.exe (Visual Studio Build Tools, “Desktop development with C++”). GNU/MinGW is not a supported target. Until there is a PyPI wheel, there is no install that skips that compile.

cd python && uv run pytest -q is now the documented path. GitHub Actions runs that same command on windows-latest (which has MSVC) and on Ubuntu; both just went green (17 tests, including the two-process Iroh sync).

If you install the C++ workload you should be able to run the suite locally the same way. Happy to hear how it feels against the scripting / query use cases you described on #1276 — this SDK is local-first (redb + Iroh), not an HTTP client in front of a server.

cursoragent and others added 9 commits August 19, 2026 17:24
Python talks to the same Rust store as the WASM and Flutter bindings:
redb on disk, signed Loro commits, no HTTP server required. v1 covers
setup, create/read/update/query/delete, and reopen-after-flush.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Indexed queries need Query::drive. Persistence tests drop every Store and
Resource handle before reopen so redb can release its file lock. destroy
tests use Folder, which has no extra required properties.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
… surface.

Do not clone python/src. Extract atomic_lib::sdk first so Kotlin, Swift,
and the Android Binder host share one API. PyO3 and FRB stay for their
languages.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
…n, not a ban.

Iroh already works in atomic_lib and Flutter. New language bindings should
not ship peer_sync as the primary API.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Local CRUD without realtime peer sync is not the product. Same surface
Flutter already wraps, plus a two-process pytest.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
start_peer is process-global; a prior in-memory start stole the node
from the P2P test. wait_for must construct timeout inside block_on.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
Same product surface as the Python SDK: local redb, signed Loro commits,
and process-global Iroh (startPeer / syncWith / live save). The ffi crate
is excluded from the workspace. Resource.destroyResource avoids UniFFI's
Disposable.destroy collision. JVM JUnit covers CRUD plus a two-process
Iroh sync; Android AAR is later.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
uv run pytest is now the documented path and works from python/ (uv.lock,
dependency-groups). A host without Visual Studio Build Tools still cannot
link the PyO3 extension; GitHub Actions runs the same command on
windows-latest, which has MSVC, and on ubuntu-latest.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
@joepio
joepio force-pushed the cursor/python-sdk-716d branch from d16da91 to 3170fb4 Compare August 19, 2026 15:24
@cursor

cursor Bot commented Aug 19, 2026

Copy link
Copy Markdown

Correction on the last note — that “not an HTTP client” line was too strong.

HTTP is part of an Atomic SDK, not a competing product. Schema validation resolves Class and Property resources: the core ontology is bundled in the local store so Folder/PlainText and the rest work offline, but anything that isn’t already there is fetched as JSON-AD over HTTP. store.get("https://…") already goes through that path in atomic_lib (and save() uses the same lookup for unknown types).

What this SDK was missing is treating that as a first-class surface: a server URL, search, and save_remote() (POST /commit) so you can script against a running AtomicServer as well as the local store. I’ll put that in rather than implying local-only.

cursoragent and others added 2 commits August 19, 2026 15:31
…otlin SDKs.

The stores were already calling atomic_lib's HTTP GET for unknown https://
subjects (that is how Class/Property validation loads remote schema). Document
and wrap that path, plus optional server= for /search and POST /commit.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
…ect.

Fetched https:// resources are stored by their own subject, which can differ
from the request URL, so has() is checked against resource.subject.

Co-authored-by: joepmeindertsma <joepmeindertsma@gmail.com>
@cursor

cursor Bot commented Aug 19, 2026

Copy link
Copy Markdown

You're right — any Atomic SDK should have HTTP, and this one already did under the hood. The core ontology is bundled so Folder / PlainText / name validate offline, but unknown Class / Property URLs go through Storelike::get_resource → HTTP GET JSON-AD. save() already used that path; the earlier "not an HTTP client" wording was wrong.

That's now an explicit part of the API, same on Python and Kotlin:

  • store.get("https://…") — HTTP GET + cache (schema and other external resources). No server= needed.
  • Store.open(path, server="https://…") / store.server = … — AtomicServer origin for /search and save_remote() (POST /commit).
  • has() stays local-only.

Local redb + Iroh stay the default. HTTP is in addition, not instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants