techne-sdk
TechneSDK for apps on the Techne platform: sign in, put, query, useEvent and the read-time check.
- npm: one package,
techne-sdk, with subpaths. - Rust: the crate
techne-sdk(crates/techne-sdk). It holds the logic that must give the platform's answers exactly: the check, the version ranking (eventState) andput's private-first, public-last plan. The npm package runs the same crate as WebAssembly, or as wasm2js on hosts without WebAssembly (Hermes). Opt-in features add the atproto plumbing services share (no platform crate either):cryptofor service-auth JWTs, signed labels, record proofs and service-account passwords, andnetfor DID resolution, verified record fetches and PDS password sessions.
The SDK depends on two things only: the published lexicons (the lexicons repo at a tag) and the platform's published API. It contains no platform code.
Install
Every import goes through the package's exports, so the resolver has to read them:
- TypeScript:
moduleResolutionnode16,nodenextorbundler. Under the oldnode(node10) mode the subpath types do not resolve. - React Native: Metro with package exports on (
unstable_enablePackageExports, the default from Expo SDK 53).techne-sdk/wasmloads the check throughtechne-sdk/wasm/load, whosereact-nativecondition picks the wasm2js build (Hermes has no WebAssembly); that file is ESM that calls Metro'srequire.
| Import | What it holds |
|---|---|
techne-sdk (also techne-sdk/generated) |
the generated XRPC client (AtpBaseClient) and every lexicon type |
techne-sdk/lexicons |
the generated lexicon dictionary (schemaDict, schemas, lexicons, ids): every lexicon document the client was built from, for reading descriptions at run time (large; keep it server side) |
techne-sdk/types/<nsid path> |
one lexicon's types, e.g. techne-sdk/types/social/scenius/getContactInvite |
techne-sdk/react |
useEvent(uri): one commons event or place as its versions, with the check's verdict |
techne-sdk/versions |
the same state without React: eventState, verifyAnswer, version labels |
techne-sdk/connect |
connect(session, platform) / connectHosted(did, fetchHandler): the clients over one session |
techne-sdk/put |
put(techne, records, { deletes }): the one write, deletes included |
techne-sdk/query |
the platform's generic queries: listRecords, getRecords, hosts |
techne-sdk/links |
link tokens: create, redeem, check, revoke |
techne-sdk/wasm |
the check itself: validate, decide, verify |
techne-sdk/catalog |
the browsable lexicon catalog |
techne-sdk/nsid, techne-sdk/nsid.json |
every NSID as a constant (NSID, METHOD) |
Datetime fields are plain strings: send Date.prototype.toISOString() (UTC, trailing Z).
Reading one event (the platform's getVersions is public, so an anonymous client will do):
import { AtpBaseClient } from 'techne-sdk'
import { useEvent } from 'techne-sdk/react'
const platform = new AtpBaseClient({ service: 'https://<platform host>' })
const load = async (uri: string) => (await platform.pub.techne.draft.commons.getVersions({ uri })).data
function Event({ uri }: { uri: string }) {
const { loading, error, fields } = useEvent(uri, { load })
if (loading) return <p>Loading</p>
if (error) return <p>Could not load it</p>
return <h1>{String(fields?.name?.value ?? '')}</h1>
}
Pass { claims } too, and verification holds the check's answer for every record (valid, signer, chain verified).
Layout
| Path | What |
|---|---|
src/ |
the TypeScript package. src/generated/, src/catalog/catalog.gen.ts and src/wasm/*.gen.* are generated and committed |
crates/techne-sdk/ |
the Rust crate, with the shared fixtures (fixtures/cases.json) both languages must answer identically |
inputs/ |
the codegen inputs, copied by scripts/inputs-sync.mjs and pinned in inputs.pin.json: inputs/lexicons/ (the lexicons repo at lexicons.tag) and inputs/platform-api/ (the platform's routed method lexicons, the atproto methods it serves and its NSID rename map, at platformApi.rev) |
lexicons-base/ |
standard atproto repo-op lexicons the generated client needs |
dev-pds/ |
a real atproto PDS and PLC in one Node process, for end-to-end runs against a platform |
scripts/ |
codegen (gen.sh), the WebAssembly build (build-wasm.sh), the package build (build.mjs), the gate |
Changing it
- A lexicon changed: tag the lexicons repo. Set
lexicons.tagandlexicons.commitininputs.pin.jsonand thetechne-lexicon-checktag inCargo.toml, then runcargo update -p techne-lexicon-checkandnode scripts/inputs-sync.mjs --lexicons <lexicons checkout> --platform <platform checkout>. Then runnpm run codegenand commit. - The platform's API changed: set
platformApi.rev(a platform tag for releases), then run the same sync and codegen. - The Rust logic changed: edit
crates/techne-sdkand runnpm run codegen(it rebuilds the WebAssembly). The fixtures must still agree.
npm run codegen needs the pinned Rust toolchain (rust-toolchain.toml) with the wasm32-unknown-unknown target, and npx for binaryen. The build is reproducible, so the gate can diff it against the committed one.
The gate
package builds dist/, then loads every subpath through exports in plain Node and runs the fixtures through the built WebAssembly and wasm2js modules. It also checks the npm pack file list with a dry run. rust runs the same fixtures through the crate.
Releasing
See RELEASING.md.