package atproto
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=50ebbbd412c55878005c39eac9493a4a3475882d43d64e685aed2164048320f2
doc/CHANGELOG.html
Changelog
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.
[Unreleased]
Added
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/serializeat://{spaceDid}/space/{type}/{skey}and the six-segment record formSpace_commit— deniable signed commits (ctx, HKDF-Expand MAC, ES256 / ES256Ksig);of_lt_hashwiresLt_hash.hashSpace_credential— delegation / space-credential / client-attestation JWTs plus DPoP helpers (reusesOauth; no DPoP nonce)Space_xrpc— draftcom.atproto.space.*query/JSON builders andClientwrappers (credential exchange, permissioned repo read/write, sync queries, notify)Space_sync— offlinelistRepoOpsapply 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).
Notes
- No lexicon pin bump. Official lexicons stay bluesky-social/atproto
f0d4877a. - Hosted-only products stay listed, not faked (see the 1.0.0 Notes below).
[1.0.2] - 2026-09-10
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.
Added
ATP_PUBLICopt-in for unauthenticated public-internet live hops (Auth.public_live_enabled, sharedPublic_live.skip_unless_public). Truthy values:1/true/yes/on. Default unset skips those tests at the start. Local TestNetwork stays onATP_LOCAL_PDS. Credential hops stay onATP_AUTH. Hosted-only products stay onATP_SPACE/ATP_PHONE/ATP_PUSH/ATP_CHAT
Changed
- Package version
1.0.1→1.0.2(dune-project/atproto.opam) dune build -p atproto @runtest/ opamwith-testis offline-safe whenATP_PUBLICis unset- GitHub Actions default TestSuite
builddoes not setATP_PUBLIC. Re-run public hops withmake test-publicor the optional PublicLive workflow (workflow_dispatchonly) - Public docs rewritten for third-party readers (#244): README product pitch and module map, warmer odoc landing,
AGENTS.md - Pin-first install, quick-start public-network note (
ATP_PUBLICgates tests only), examples index includesexamples/client-metadata.json, LICENSE / Issues / SECURITY links, and a product-pitch opam description
Notes
- Experimental 0016 helpers stay under Unreleased
[1.0.1] - 2026-09-10
Released 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.
Changed
- Package version
1.0.0→1.0.1(dune-project/atproto.opam) digestiflower bound>= 1.1.2(Digestif.SHA256/digest_string/hmac_string/to_raw_string; unbounded digestif can solve to 0.5-era and fail withUnbound module Digestif)mirage-crypto-ecandmirage-crypto-rnglower bounds>= 1.2.0.P256.Dsa.pub_of_octetsis the 1.x octets API (1.0.0+; 0.11 usedof_cstruct). This repo also callsMirage_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 floor- Official NSID coverage (
test_lexicon_coverage) moves off@runtestto@lexicon-coverage.dune build -p atproto @runtest/ opamwith-testrun unit tests only. TestSuite still runs the gate (dune build @lexicon-coverage)
Notes
- opam-repository submissions should omit the redundant
version:field (opam lint). Keep(version ...)indune-project; the in-repo generatedatproto.opammay still carryversion: - No
x-ci-accept-failures. The coverage gate is not deleted - Experimental 0016 helpers stay under Unreleased
[1.0.0] - 2026-09-09
First 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.gitAdded
OCaml 5 / packaging
- OCaml 5 / packaging modernization (#229): dune lang 3.11, OCaml
>= 4.14.1and< 5.4, Jane Street>= v0.16.0and< v0.18~, CIbuildon 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 OAuth
- Public HTTPS client-metadata + production browser-login path (#230):
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.mlis offline scaffolding. The application still hosts the HTTPS document and redirect URI. This library does not host a login UI
Chat (hosted chat.bsky.*)
- Hosted Bluesky chat client (#231):
Oauth.default_chat_scope/Oauth_scope.has_chat/Chat.service_aud/list_convos_body/list_convos_service/get_messages_service/send_message_serviceonapi.bsky.chat.examples/chat_production.mlis offline wiring. No OSS chat backend is started or stubbed
Video (hosted app.bsky.video.*)
- Hosted Bluesky video client (#232):
Video.pds_audiencefrom#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.mlis offline wiring. No hosted transcoder is started or stubbed
Repo_sync / TAP-like indexer
- Library-ready indexer / backfill path (#233):
Repo_sync.export_record_proof/export_record_proof_bytes/walk_json/record_json/status_to_string,Dag_cbor.to_yojson.examples/repo_sync_indexer.mlis an offline fixture sketch. Not a hosted Tap
Jetstream archive token
- Archive HTTP operator-token env (#234):
JETSTREAM_API_KEY/JETSTREAM_ARCHIVE_TOKEN/~token→Authorization: Bearer;require_archive_token/~require_token.examples/jetstream_archive.mlis offline wiring. The operator must supply the key. This library does not invent one
Phone / contacts / push
- Hosted phone / contacts / push client (#235):
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_PUSHskip gates.examples/contacts_production.mlis offline wiring. No SMS gateway and no APNs/FCM.requestPhoneVerificationis not faked
Docs
- Human-readable Keep a Changelog history (#236):
## [0.1.0]thematic sections for the tagged RC. This 1.0.0 section is the packaged surface since that tag
Changed
- Package version
0.1.0→1.0.0(dune-project/atproto.opam) - OCaml upper bound lifted from
< 5.0to< 5.4(#229)
Fixed
- CI
Install libzstddrops every GitHub-runner apt source that points atdl.google.com, so a stale Packages hash cannot fail required jobs (#230)
Notes
Honesty constraints for this 1.0.0 surface:
- No fake OSS chat backend, video transcoder, Tap host, SMS gateway, or APNs/FCM push backend
- The app still hosts HTTPS
client-metadata.json(that URL isclient_id) and receives the browser redirect - Jetstream archive HTTP on Bluesky-hosted instances needs an operator API key (
JETSTREAM_API_KEY/JETSTREAM_ARCHIVE_TOKEN, or~token). The library does not invent one - No lexicon pin bump in the #229–#236 series (pin stays
f0d4877a). Newly published official lexicons after that SHA fail the coverage gate until the snapshot and bindings (or an explicit skip) are updated - Hosted-only SMS / APNs-FCM / unhosted feed generator stay listed not faked (
requestPhoneVerificationis not faked)
Not in this release
- An application still has to host the HTTPS
client-metadata.jsonand receive the browser redirect (this library builds/validates the document and drives authorize → code → token; it does not host them or a login UI) - Hosted Tap service (the indexer / backfill library path is documented and library-ready; this package still does not fake a Tap host)
- Hosted video transcoder (the production client path is documented and library-ready; this package still does not fake a local transcoder)
- Official OSS chat backend (TestNetwork does not start one). The production hosted path is documented and library-ready; this package still does not fake a local chat service
- Newly published official lexicons after bluesky-social/atproto
f0d4877a— the coverage gate fails until the pin snapshot and bindings (or an explicit skip) are updated - Jetstream archive HTTP download on Bluesky-hosted instances still requires the operator to supply an API key
- Hosted SMS / phone-verification gateway (the production client path is documented and library-ready; this package still does not fake
requestPhoneVerificationor a local SMS service) - Official OSS push gateway (official Bluesky push is closed to the official app; the production client path is documented and library-ready; this package still does not fake APNs/FCM)
- Permissioned data / spaces / LtHash (no stable public spec yet)
[0.1.0] - 2026-09-09
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).
Added
Protocol client
- XRPC GET/POST (Cohttp) plus HTTP/2 TLS (
Http_client) for public HTTPS - Session / JWT (
Auth,Session), includingauthFactorToken/allowTakendownand typedgetSession - AppView: actor, feed, graph, bookmark, notification, labeler, unspecced, drafts, contacts, age assurance
- Repo writes and typed record builders (
Repo,Records) - Identity, DID PLC/web/key, CID/CAR/DAG-CBOR, MST, TID, AT URI, firehose
- Lexicon 1 parse / validate /
to_ocaml, including bundled official documents (pinf0d4877a) site.standard.*andcom.germnetwork.declarationrecord builderscom.atproto.adminandcom.atproto.tempclients (no invented operator session)- Local TestNetwork via published
@atproto/dev-env@0.6.4(make test-pds/ATP_REQUIRE_LOCAL_PDS=1)
OAuth / DPoP
- PKCE S256, DPoP ES256 + nonce, PAR, token, refresh, RFC 7009 revoke, granular scopes, official
app.bsky.auth*/chat.bsky.authFullChatClientpermission-sets - Public HTTPS client-metadata + production browser-login path (#230):
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.mlis offline scaffolding. The application still hosts the HTTPS document and redirect URI. This library does not host a login UI. - Local TestNetwork loopback metadata, AS discovery, PAR, authorize GET,
~api/sign-in/~api/consentwith real cookies, DPoPgetSession, AppView / OzonegetServiceAuth(DPoP cannot be proxied) - Chat-capable scopes (#231):
Oauth.default_chat_scope/Oauth_scope.has_chat/full_chat_client_scope.Oauth.default_scopeis not enough for DMs.
Chat (hosted chat.bsky.*)
- Hosted Bluesky chat client (#231):
Chat.service_aud/default_host/ATP_CHAT_HOST, query-pair helpers shared with service-authlist_convos_service/get_convo_service/get_messages_service/send_message_serviceonapi.bsky.chat. Password sessions still sendatproto-proxy.examples/chat_production.mlis offline wiring. - No OSS chat backend is started or stubbed. Official TestNetwork 0.6.4 still has none (
ozone.chatUrl=localhost:2590, “must run separate chat service”). Live DM tests stay skippable unlessATP_AUTHhas a chat/DM scope orATP_CHAT=1.
Video (hosted app.bsky.video.*)
- Hosted Bluesky video client (#232):
Video.pds_audience(session#atproto_pds, notdid: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.mlis offline wiring. - No hosted transcoder is started or stubbed. Official TestNetwork still has none. Live
getUploadLimitsstays skippable unlessATP_AUTHis a real credential.
Repo_sync / TAP-like indexer
- Library-ready indexer / backfill path (#233):
Repo_sync.export_record_proof/export_record_proof_bytes/walk_json/record_json/status_to_string,Dag_cbor.to_yojson, firehose apply,#syncdesync, Sync 1.1 export, offlinewrite_signed_repo.examples/repo_sync_indexer.mlis an offline fixture sketch. - Not a hosted Tap. Official TestNetwork is a local PDS + AppView + Ozone stack, not a Tap host.
Jetstream
- v2 live tail, collection/DID/kind filters, seq + unix-µs cursors, reconnect/dedupe, v1
/subscribecompat - Live dict-zstd
subscribeEvents(#109): Jane Streetzstandard,getZstdDictionary, v2zstdDictionary=<id> - v2
subscribe/subscribe_oneofferSec-WebSocket-Protocol: xrpc.v1.json(#110); RFC 6455 §4.1 echo required. Unoffered connections unchanged. v2 stays server-push only - Archive HTTP operator-token env (#234):
JETSTREAM_API_KEY/JETSTREAM_ARCHIVE_TOKEN/~token→Authorization: Bearer;require_archive_token/~require_token.examples/jetstream_archive.mlis offline wiring. The operator must supply the key. This library does not invent one. LivesubscribeEventsstays unauthenticated. Self-hosted archives may omit the key.
Phone / contacts / push (hosted AppView)
- Hosted phone / contacts / push client (#235):
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_PUSHskip gates.examples/contacts_production.mlis offline wiring. - No SMS gateway and no APNs/FCM. Official TestNetwork still has no phone or push service.
requestPhoneVerificationis not faked. Official Bluesky push is closed to the official app; third-party clients host their own gateway.
Ozone
- Typed encodings:
emit_event/emit_event_service_typed(#187, #188),get_account_preferencesviaActor.preferences(#186),create_activity(#191),schedule_action(#196). Raw Yojson siblings stay unchanged. - Yojson body / query-pair helpers for scheduled actions, communication templates, safelink, report, set/setting/team, queue,
query_events/query_statuses, leftoverquery_reports/search_repos(#197, #211–#216, #219, #225). Queue assign/unassign/route only — notreport.assignModerator. Does not invent leftover unusedmanagerRole/lang. - Live TestNetwork hops for communication templates / sets (#119), leftover queue / report / setting NSIDs (#123, #129, #154),
getAccountPreferences(#137). Skip when the NSID is not served or TestNetwork policy applies. Does not fake a hosted ozone store.
Client XRPC migration and leftover helpers
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.
- Repo create/put/delete Yojson helpers and
blob_ref_to_json(#192–#194) - Moderation
create_reportYojson bodies (#195) - Graph mute/unmute Client + list/thread mute bodies (#198, #209)
- Label
queryLabels, Actor profile/search/suggestions (#199, #200) - Repo / Feed / Notification / Server / Session / Identity / Sync /
getServiceAuthleftover JSON via Client (#201–#208) - Notification
putPreferencesv1 +putActivitySubscriptionbodies (#210); leftoverlist_notificationsquery pairs (#226) - Label
label_value_definitionencode + Labelerpolicies_to_json(#217) - Site typed record encodes (#218)
- Typed Actor
putPreferencesand Draft create/update (#185, #190) - Xrpc
x-atproto-bsky-topicshelpers (#189) - Feed / Graph / Unspecced leftover AppView query-pair helpers (#220–#224, #227): currently sent fields only; no leftover unused lexicon fields invented
Local TestNetwork coverage
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.
- PDS handle update, activate/deactivate, AppView drafts / list feed / mute list / notification prefs (#116)
- Leftover AppView / PDS / ozone NSIDs after that (#123, #129)
- Remaining
com.atproto.admin/com.atproto.server/com.atproto.tempoperator NSIDs (#150, #152, #162) - AppView
sendInteractions/describeFeedGenerator/putPreferencesv1, leftovergetFeedSkeleton, unspecced age-assurance (#153, #163, #168) interestsPref.updatedAtget/put preferences hops (#184; parse shipped in #181)
Docs, CI, and packaging
- odoc HTML on GitHub Pages (#112, #113): https://david-engelmann.github.io/atproto/
- Function-level odoc and landing-map sweep across public modules (#114, #117, #122, #125–#126, #128, #130–#136, #138, #140–#149, #151, #156–#161, #165–#166, #170, #177)
- Compiled-only
examples/offline.mlconstructor/parser coverage (#171) - Installed-package consumer smoke (#172) — not an opam-repository publish
- libzstd install notes (#174, #176)
- merge-when-green squash automation (#179); lexicon-pin drift is advisory and must not deadlock merges (#182)
- Lexicon pin
f0d4877afor actor interestsupdatedAt(#132, #181) - Docs-only notes hygiene PRs that previously restated this file (#121, #127, #139, #155, #164, #167, #169, #173, #175, #178, #180, #183)
- opam-repository publish prep (#228): package description / README / CONTRIBUTING / odoc landing say the package is published on opam-repository
- OCaml 5 / packaging modernization hop toward 1.0.0 (#229): dune lang 3.11, OCaml
>= 4.14.1and< 5.4, Jane Street>= v0.16.0and< v0.18~, CIbuildon 4.14.1 + 5.3.0. ocamlformat stays 0.25.1. Package version stays0.1.0
Changed
- OCaml upper bound lifted from
< 5.0to< 5.4(#229). Public Jane Street v0.17 does not support OCaml 5.4+; 5.0 is untested (v0.17 needs 5.1+) - Leftover JSON XRPC callers share
Clienthelpers and*_bodyquery/POST builders instead of hand-rolled Cohttp (#192–#227). Empty procedure output stays"". String create/put/delete record APIs stay Ozone.get_account_preferencespreferences areActor.preference list(compile-breaking; no backwards-compat) (#186)
Fixed
- CI
Install libzstddrops every GitHub-runner apt source that points atdl.google.com, so a stale Packages hash cannot fail required jobs (#230) Identity.resolve_did/resolve_identityfall back to PLC /did:webwhen the host returnsMethodNotImplemented(PDS 0.5.x and current entryway)
Notes
Honesty constraints for this 0.1.0 surface:
- No fake OSS chat backend, video transcoder, Tap host, SMS gateway, or APNs/FCM push backend
- The app still hosts HTTPS
client-metadata.json(that URL isclient_id) and receives the browser redirect - Jetstream archive HTTP on Bluesky-hosted instances needs an operator API key (
JETSTREAM_API_KEY/JETSTREAM_ARCHIVE_TOKEN, or~token). The library does not invent one - No lexicon pin bump in the #228–#235 production-path series (pin stays
f0d4877a). Newly published official lexicons after that SHA fail the coverage gate until the snapshot and bindings (or an explicit skip) are updated - Hosted-only SMS / APNs-FCM / unhosted feed generator stay listed not faked (
requestPhoneVerificationis not faked)
examples/offline.ml typechecks against the public API under dune build / dune runtest.
PR index (#112–#235)
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.
Not in this release
- An application still has to host the HTTPS
client-metadata.jsonand receive the browser redirect (this library builds/validates the document and drives authorize → code → token; it does not host them or a login UI) - Hosted Tap service (the indexer / backfill library path is documented and library-ready; this package still does not fake a Tap host)
- Hosted video transcoder (the production client path is documented and library-ready; this package still does not fake a local transcoder)
- Official OSS chat backend (TestNetwork does not start one). The production hosted path is documented and library-ready; this package still does not fake a local chat service
- Newly published official lexicons after bluesky-social/atproto
f0d4877a— the coverage gate fails until the pin snapshot and bindings (or an explicit skip) are updated - Jetstream archive HTTP download on Bluesky-hosted instances still requires the operator to supply an API key. Live compressed
subscribeEventsandxrpc.v1.jsonare implemented. Self-hosted archives may omit the key - Hosted SMS / phone-verification gateway (the production client path is documented and library-ready; this package still does not fake
requestPhoneVerificationor a local SMS service) - Official OSS push gateway (official Bluesky push is closed to the official app; the production client path is documented and library-ready; this package still does not fake APNs/FCM)
- Permissioned data / spaces / LtHash (no stable public spec yet)