All notable changes to this project are documented in this file.
The format is based on Keep a Changelog. Current package version is 1.0.2 (dune-project / atproto.opam). Tagged 1.0.1 is at 53ffbc2. The next published package is atproto.1.0.2; it supersedes ocaml/opam-repository#30703.
This file is the human-readable release history. PR numbers are included sparingly so a change can be traced; they are not a substitute for git log.
Experimental permissioned-data / spaces helpers from proposal 0016. Present on the 1.0.1 / 1.0.2 trees, but not a spaces product API. The proposal is not final. This library does not start or stub a space host. Live hops skip unless ATP_SPACE=1 and ATP_SPACE_HOST names a real host.
Lt_hash β 0016 commit digest (1024-lane LtHash; official empty / one / two snapshot vectors from bluesky-social/atproto#5187)At_uri.Space β parse/serialize at://{spaceDid}/space/{type}/{skey} and the six-segment record formSpace_commit β deniable signed commits (ctx, HKDF-Expand MAC, ES256 / ES256K sig); of_lt_hash wires Lt_hash.hashSpace_credential β delegation / space-credential / client-attestation JWTs plus DPoP helpers (reuses Oauth; no DPoP nonce)Space_xrpc β draft com.atproto.space.* query/JSON builders and Client wrappers (credential exchange, permissioned repo read/write, sync queries, notify)Space_sync β offline listRepoOps apply and two-root CAR encode/apply (signed commit, then DRISL index, then records)Deferred: com.atproto.simplespace.*, space: OAuth scopes, and proposal registerNotify repo (not in the #5187 lexicon).
f0d4877a.Opam-health follow-up to tagged 1.0.1. The 1.0.1 package built, linted, and passed lower-bounds; opam-ci tests failed because unauthenticated live hops (PLC directory, AppView, public PDS, firehose / Jetstream) ran by default and only skipped on a caught exception.
1.0.2 makes opam install -t / dune build -p atproto @runtest offline-safe. The published package will be atproto.1.0.2; it supersedes ocaml/opam-repository#30703.
No lexicon pin bump (official pin stays f0d4877a). Jane Street / OCaml bounds are unchanged. No fake hosts.
ATP_PUBLIC opt-in for unauthenticated public-internet live hops (Auth.public_live_enabled, shared Public_live.skip_unless_public). Truthy values: 1 / true / yes / on. Default unset skips those tests at the start. Local TestNetwork stays on ATP_LOCAL_PDS. Credential hops stay on ATP_AUTH. Hosted-only products stay on ATP_SPACE / ATP_PHONE / ATP_PUSH / ATP_CHAT1.0.1 β 1.0.2 (dune-project / atproto.opam)dune build -p atproto @runtest / opam with-test is offline-safe when ATP_PUBLIC is unsetbuild does not set ATP_PUBLIC. Re-run public hops with make test-public or the optional PublicLive workflow (workflow_dispatch only)AGENTS.mdATP_PUBLIC gates tests only), examples index includes examples/client-metadata.json, LICENSE / Issues / SECURITY links, and a product-pitch opam descriptionReleased as tag 1.0.1 (53ffbc2 / #243). Public opam publish: ocaml/opam-repository#30703 (open; supersedes #30698).
Packaging fix so opam-ci can install and test the package. The 1.0.0 submission built on 4.14 / 5.2 but failed on the lexicon coverage gate under with-test, unbounded digestif / mirage-crypto-ec lower bounds, and a redundant version: field in the submitted opam file. OCaml 5.4+ remains out of range (Jane Street v0.17).
No lexicon pin bump (official pin stays f0d4877a). Jane Street / OCaml bounds are unchanged. Experimental 0016 helpers are documented under Unreleased β present on this tree, not a product API.
1.0.0 β 1.0.1 (dune-project / atproto.opam)digestif lower bound >= 1.1.2 (Digestif.SHA256 / digest_string / hmac_string / to_raw_string; unbounded digestif can solve to 0.5-era and fail with Unbound module Digestif)mirage-crypto-ec and mirage-crypto-rng lower bounds >= 1.2.0. P256.Dsa.pub_of_octets is the 1.x octets API (1.0.0+; 0.11 used of_cstruct). This repo also calls Mirage_crypto_rng_unix.use_default (added in 1.2.0). The 1.x mirage-crypto packages pin each other, so 1.2.0 is the coherent floortest_lexicon_coverage) moves off @runtest to @lexicon-coverage. dune build -p atproto @runtest / opam with-test run unit tests only. TestSuite still runs the gate (dune build @lexicon-coverage)version: field (opam lint). Keep (version ...) in dune-project; the in-repo generated atproto.opam may still carry version:x-ci-accept-failures. The coverage gate is not deletedFirst stable packaged surface, tagged at 1.0.0 (8a1b866 / #237). What is new since tagged 0.1.0 (8f44fb9 / #228). The first 1.0.0 opam-repository submission was #30698 (superseded by 1.0.1 / #30703). The 0.1.0 opam-repository PR #30695 is separate.
Requires OCaml >= 4.14.1 and < 5.4 (CI build: 4.14.1 and 5.3.0). Jane Street core / async / ppx_jane / zstandard are >= v0.16.0 and < v0.18~ (v0.16 on 4.14, v0.17 on 5.1β5.3). ocamlformat stays 0.25.1. Official lexicon pin stays f0d4877a (no bump in #229β#236). System libzstd is required for Jetstream dict-zstd (Ubuntu/Debian libzstd-dev, Homebrew zstd).
A GitHub pin is the 1.0.0 development surface:
opam pin add atproto git+https://github.com/david-engelmann/atproto.git>= 4.14.1 and < 5.4, Jane Street >= v0.16.0 and < v0.18~, CI build on 4.14.1 + 5.3.0. ocamlformat stays 0.25.1. Public Jane Street v0.17 does not support OCaml 5.4+; 5.0 is untested (v0.17 needs 5.1+)https_client_id / public_https_metadata / validate_https_metadata / metadata_document / metadata_http_response / fetch_client_metadata / start_browser_login / complete_browser_login. examples/oauth_https_metadata.ml is offline scaffolding. The application still hosts the HTTPS document and redirect URI. This library does not host a login UIchat.bsky.*)Oauth.default_chat_scope / Oauth_scope.has_chat / Chat.service_aud / list_convos_body / list_convos_service / get_messages_service / send_message_service on api.bsky.chat. examples/chat_production.ml is offline wiring. No OSS chat backend is started or stubbedapp.bsky.video.*)Video.pds_audience from #atproto_pds / upload_service_auth_body / mint_upload_token / get_upload_limits_service / get_job_status_body / upload_video_body / part_slice / embed_of_blob / embed_of_job. examples/video_production.ml is offline wiring. No hosted transcoder is started or stubbedRepo_sync.export_record_proof / export_record_proof_bytes / walk_json / record_json / status_to_string, Dag_cbor.to_yojson. examples/repo_sync_indexer.ml is an offline fixture sketch. Not a hosted TapJETSTREAM_API_KEY / JETSTREAM_ARCHIVE_TOKEN / ~token β Authorization: Bearer; require_archive_token / ~require_token. examples/jetstream_archive.ml is offline wiring. The operator must supply the key. This library does not invent oneContact.get_matches_body / get_matches_appview / *_service, Notification.register_push_body / unregister_push_body / platform_ios / effective_push_proxy / Xrpc.notif_proxy, ATP_PHONE / ATP_PUSH skip gates. examples/contacts_production.ml is offline wiring. No SMS gateway and no APNs/FCM. requestPhoneVerification is not faked## [0.1.0] thematic sections for the tagged RC. This 1.0.0 section is the packaged surface since that tag0.1.0 β 1.0.0 (dune-project / atproto.opam)< 5.0 to < 5.4 (#229)Install libzstd drops every GitHub-runner apt source that points at dl.google.com, so a stale Packages hash cannot fail required jobs (#230)Honesty constraints for this 1.0.0 surface:
client-metadata.json (that URL is client_id) and receives the browser redirectJETSTREAM_API_KEY / JETSTREAM_ARCHIVE_TOKEN, or ~token). The library does not invent onef0d4877a). Newly published official lexicons after that SHA fail the coverage gate until the snapshot and bindings (or an explicit skip) are updatedrequestPhoneVerification is not faked)client-metadata.json and receive the browser redirect (this library builds/validates the document and drives authorize β code β token; it does not host them or a login UI)f0d4877a β the coverage gate fails until the pin snapshot and bindings (or an explicit skip) are updatedrequestPhoneVerification or a local SMS service)First public opam release candidate, tagged at #228 (8f44fb9). The packaged GitHub surface is now 1.0.0.
The 0.1.0 opam-repository PR #30695 was superseded by 1.0.0 / 1.0.1. A GitHub pin still works for development:
opam pin add atproto git+https://github.com/david-engelmann/atproto.gitRequires OCaml >= 4.14.1 and < 5.4 (CI build: 4.14.1 and 5.3.0). Jane Street core / async / ppx_jane / zstandard are >= v0.16.0 and < v0.18~ (v0.16 on 4.14, v0.17 on 5.1β5.3). System libzstd is required for Jetstream dict-zstd (Ubuntu/Debian libzstd-dev, Homebrew zstd).
The first installable cut was 2026-09-03. Work in #229β#236 shipped on main after the 0.1.0 tag while the package version was still 0.1.0; that wave is packaged as 1.0.0. The sections below remain the human-readable 0.1.0-era history (through #235).
Http_client) for public HTTPSAuth, Session), including authFactorToken / allowTakendown and typed getSessionRepo, Records)to_ocaml, including bundled official documents (pin f0d4877a)site.standard.* and com.germnetwork.declaration record builderscom.atproto.admin and com.atproto.temp clients (no invented operator session)@atproto/dev-env@0.6.4 (make test-pds / ATP_REQUIRE_LOCAL_PDS=1)app.bsky.auth* / chat.bsky.authFullChatClient permission-setshttps_client_id / public_https_metadata / validate_https_metadata / metadata_document / metadata_http_response / fetch_client_metadata / start_browser_login / complete_browser_login. examples/oauth_https_metadata.ml is offline scaffolding. The application still hosts the HTTPS document and redirect URI. This library does not host a login UI.~api/sign-in / ~api/consent with real cookies, DPoP getSession, AppView / Ozone getServiceAuth (DPoP cannot be proxied)Oauth.default_chat_scope / Oauth_scope.has_chat / full_chat_client_scope. Oauth.default_scope is not enough for DMs.chat.bsky.*)Chat.service_aud / default_host / ATP_CHAT_HOST, query-pair helpers shared with service-auth list_convos_service / get_convo_service / get_messages_service / send_message_service on api.bsky.chat. Password sessions still send atproto-proxy. examples/chat_production.ml is offline wiring.ozone.chatUrl = localhost:2590, βmust run separate chat serviceβ). Live DM tests stay skippable unless ATP_AUTH has a chat/DM scope or ATP_CHAT=1.app.bsky.video.*)Video.pds_audience (session #atproto_pds, not did:web:video.bsky.app), upload_service_auth_body / mint_upload_token (lxm = com.atproto.repo.uploadBlob), get_upload_limits_service, job / multipart helpers (part_slice), embed_of_blob / embed_of_job (create embed is the blob ref, not the playlist). examples/video_production.ml is offline wiring.getUploadLimits stays skippable unless ATP_AUTH is a real credential.Repo_sync.export_record_proof / export_record_proof_bytes / walk_json / record_json / status_to_string, Dag_cbor.to_yojson, firehose apply, #sync desync, Sync 1.1 export, offline write_signed_repo. examples/repo_sync_indexer.ml is an offline fixture sketch./subscribe compatsubscribeEvents (#109): Jane Street zstandard, getZstdDictionary, v2 zstdDictionary=<id>subscribe / subscribe_one offer Sec-WebSocket-Protocol: xrpc.v1.json (#110); RFC 6455 Β§4.1 echo required. Unoffered connections unchanged. v2 stays server-push onlyJETSTREAM_API_KEY / JETSTREAM_ARCHIVE_TOKEN / ~token β Authorization: Bearer; require_archive_token / ~require_token. examples/jetstream_archive.ml is offline wiring. The operator must supply the key. This library does not invent one. Live subscribeEvents stays unauthenticated. Self-hosted archives may omit the key.Contact.get_matches_body / get_matches_appview / *_service, Notification.register_push_body / unregister_push_body / platform_ios / effective_push_proxy / Xrpc.notif_proxy, ATP_PHONE / ATP_PUSH skip gates. examples/contacts_production.ml is offline wiring.requestPhoneVerification is not faked. Official Bluesky push is closed to the official app; third-party clients host their own gateway.emit_event / emit_event_service_typed (#187, #188), get_account_preferences via Actor.preferences (#186), create_activity (#191), schedule_action (#196). Raw Yojson siblings stay unchanged.query_events / query_statuses, leftover query_reports / search_repos (#197, #211β#216, #219, #225). Queue assign/unassign/route only β not report.assignModerator. Does not invent leftover unused managerRole / lang.getAccountPreferences (#137). Skip when the NSID is not served or TestNetwork policy applies. Does not fake a hosted ozone store.JSON XRPC that still hand-rolled Cohttp now shares Client.get_json / Client.get_text / Client.post_json and query-pair or POST body helpers. Public signatures and parse types stay unchanged unless noted. Binary upload_blob / import_repo and Sync CAR stay Cohttp.
blob_ref_to_json (#192β#194)create_report Yojson bodies (#195)queryLabels, Actor profile/search/suggestions (#199, #200)getServiceAuth leftover JSON via Client (#201β#208)putPreferences v1 + putActivitySubscription bodies (#210); leftover list_notifications query pairs (#226)label_value_definition encode + Labeler policies_to_json (#217)putPreferences and Draft create/update (#185, #190)x-atproto-bsky-topics helpers (#189)Live hops against @atproto/dev-env@0.6.4. Skip only when the NSID is not served or TestNetwork policy applies (email-token, UpstreamFailure, feature-disabled). Throwaway accounts never use alice.test for destructive calls. Hosted chat / video / Tap / SMS / push stay listed not faked.
com.atproto.admin / com.atproto.server / com.atproto.temp operator NSIDs (#150, #152, #162)sendInteractions / describeFeedGenerator / putPreferences v1, leftover getFeedSkeleton, unspecced age-assurance (#153, #163, #168)interestsPref.updatedAt get/put preferences hops (#184; parse shipped in #181)examples/offline.ml constructor/parser coverage (#171)f0d4877a for actor interests updatedAt (#132, #181)>= 4.14.1 and < 5.4, Jane Street >= v0.16.0 and < v0.18~, CI build on 4.14.1 + 5.3.0. ocamlformat stays 0.25.1. Package version stays 0.1.0< 5.0 to < 5.4 (#229). Public Jane Street v0.17 does not support OCaml 5.4+; 5.0 is untested (v0.17 needs 5.1+)Client helpers and *_body query/POST builders instead of hand-rolled Cohttp (#192β#227). Empty procedure output stays "". String create/put/delete record APIs stayOzone.get_account_preferences preferences are Actor.preference list (compile-breaking; no backwards-compat) (#186)Install libzstd drops every GitHub-runner apt source that points at dl.google.com, so a stale Packages hash cannot fail required jobs (#230)Identity.resolve_did / resolve_identity fall back to PLC / did:web when the host returns MethodNotImplemented (PDS 0.5.x and current entryway)Honesty constraints for this 0.1.0 surface:
client-metadata.json (that URL is client_id) and receives the browser redirectJETSTREAM_API_KEY / JETSTREAM_ARCHIVE_TOKEN, or ~token). The library does not invent onef0d4877a). Newly published official lexicons after that SHA fail the coverage gate until the snapshot and bindings (or an explicit skip) are updatedrequestPhoneVerification is not faked)examples/offline.ml typechecks against the public API under dune build / dune runtest.
Grouped so the old βon top of #Nβ sentence is recoverable without rereading it:
Theme | Pull requests |
|---|---|
Docs / Pages / odoc / CI | #112β#114, #117, #120β#122, #125β#128, #130β#136, #138β#149, #151, #155β#161, #164β#183 |
Live TestNetwork leftovers | #116, #119, #123, #129, #137, #150, #152β#154, #162β#163, #168, #184 |
Lexicon pin | |
Typed encodings / leftover Client helpers | |
Packaging / OCaml 5 | |
OAuth HTTPS metadata | |
Hosted chat | |
Hosted video | |
Repo_sync / TAP-like indexer | |
Jetstream archive token | |
Phone / contacts / push |
Earlier Jetstream work that shipped in the 2026-09-03 cut: #109β#110.
client-metadata.json and receive the browser redirect (this library builds/validates the document and drives authorize β code β token; it does not host them or a login UI)f0d4877a β the coverage gate fails until the pin snapshot and bindings (or an explicit skip) are updatedsubscribeEvents and xrpc.v1.json are implemented. Self-hosted archives may omit the keyrequestPhoneVerification or a local SMS service)