@s3p/sdk
v0.17.1
Published
Browser SDK for s3p — collections, docs, logs, files, auth, RPC, roles. S3 is the database; IAM is the authorization.
Readme
@s3p/sdk
Browser SDK for the S3-Native App Platform. Apps import this module;
it hides the Cognito auth → Cognito Identity Pool → STS handshake
and exposes a typed data-shaped surface (collection, log, doc,
view, files, fn, transaction, plus admin facades for Cognito
users, operators, and IAM roles).
Substrate-agnostic data primitives (lists, logs, rollups,
envelope-encryption adapter) live in
../s3db/ and are re-exported here. The SDK is a thin
composer of s3db + Cognito + IAM.
Public entries
| Entry | Purpose |
|---|---|
| @s3p/sdk | The main connect() + app.* surface for app code (browser-only). |
| @s3p/sdk/raw | Raw @aws-sdk/client-s3 clients + commands. Visible escape hatch. |
| @s3p/sdk/functions | Server-function authoring contract (defineFunction, FnContext types). |
| @s3p/sdk/server-runtime | The Node runtime the deploy bundles into each project's Lambda (makeHandler, ctx.db/ctx.secrets). |
| @s3p/sdk/conventions | Bucket naming, ROOT_SLUG, hosted-UI/KMS-alias derivation. Node-importable. |
| @s3p/sdk/policy-templates | IAM policy renderers + capabilities. Used by the CLI and app.roleAdmin. |
| @s3p/sdk/contracts | Shared config/manifest shapes + renderProjectModule — the ONE allowlist renderer of the public boot module (/__s3p/project.js), used by s3p build/dev/deploy AND provisioning. |
| @s3p/sdk/paths | paths() — THE one place that knows the bucket layout. |
| @s3p/sdk/email-templates | Pre-rendered default Cognito/email templates. |
| @s3p/sdk/email-limits | The send-cap logic (per-user/project daily + monthly quotas). Pure, tested; enforced by every runtime send path. |
| @s3p/sdk/bundle-functions | Deploy-side bundling of functions/*.ts into the runtime Lambda (+ built-ins like send-email). |
| @s3p/sdk/device-link | The s3p connect relay crypto (ECDH-ES P-256 + AES-GCM sealing). |
| @s3p/sdk/domain-contract | The one shared wire contract for domain requests (CLI + browser write the same bytes). |
| @s3p/sdk/signup-policy | Signup-volume policy shapes/logic. |
| @s3p/sdk/db-inspect | Store discovery over a bucket's s3db layout (feeds the console's Stores/Permissions panels). |
| @s3p/sdk/interchange | The cross-project request channel shapes. |
| @s3p/sdk/platform-version | Generated versions + PLATFORM_REV + compat floor. |
The split keeps Node-importable convention/policy code out of the browser SDK bundle, while letting both consumers share one source of truth. Anything that imports the root entry gets the full browser runtime and won't work in Node.
Connect
import { connect } from "/sdk/index.js"; // the path the app is SERVED at
const app = await connect(); // reads /__s3p/project.jsconnect() reads the project's public boot module
(/__s3p/project.js, written by s3p build/deploy and rendered by
renderProjectModule), signs in natively against the project's pool,
and exchanges the ID token at the Identity Pool for STS credentials.
The SDK's toPlatformConfig deliberately strips consoleAuth for app
boot — apps sign into their own pool; only the console (operator
federation) uses the platform pool.
App surface
// Identity
app.me // { sub, username, email, groups[], role } | null
app.signedIn
// NATIVE auth (default — no hosted UI, no redirect)
await app.auth.signIn(email, password)
await app.auth.signUp(email, password); await app.auth.confirmSignUp(email, code)
await app.auth.forgotPassword(email); await app.auth.respondToChallenge({...})
await app.auth.signOut()
// app.signIn()/app.signOut() — the hosted-UI redirect flow: the third-party
// IdP path. Google is LIVE (`app.signIn({ identityProvider: "Google" })`
// backs the /signin button); dormant for everything else.
// Data primitives
app.collection<T>(name, opts?) // keyed JSON; scope: "shared"|"private"|"admin"
app.log<T>(name, opts?) // ULID-keyed append-only, partition: "author"
app.doc<T>(name, opts?) // single named blob, {token} substitution
app.view(def) // DuckDB-WASM over Parquet rollups
app.files.{put,get,objectUrl,list,delete} // blob storage under files/
// RPC + transactions
await app.fn(name)(input) // invoke a server function (per-project Lambda)
app.functions // registry, logs, invocation ledger
app.transaction([...steps]) // best-effort sequential, NOT ACID
// Email + domains
await app.email.send({...}) // built-in send-email function, SESv2
app.domains.{add, verify, list, status, remove}
app.domains.{senders, useForAuth, setSenderName} // who your mail comes from
app.origins.{list, add, remove} // browser origins allowed to reach your data
// — for native shells (Capacitor/Tauri/
// Electron), whose origin has no DNS
// Secrets (runtime-decryptable only)
app.secrets.put(name, value); app.secrets.get(name)
// Admin facades (IAM-gated)
app.users.* // THIS project's own pool ("App users")
app.operators.* // platform identities allowed into the console ("Console users")
app.roleAdmin.* // list / save / sync / whoCan over s3p.permissions.json
// Dev guards (raise on accidental large scans)
setDevGuards({ warnAtCount: 100 })See ../../docs/SDK.md for the full method-level
reference, ../../DATA.md for the data model, and
../../CLAUDE.md for the architectural why.
Why bundle to a single file
The platform pitch is "apps need no build step." Each app is a static
bundle that imports the SDK from /sdk/index.js — s3p build
copies the installed package there. The SDK ships as one bundled
dist/index.js (+ source map + .d.ts), with the AWS SDK kept
external as a jsDelivr URL so it's CDN-cached across apps.
Layout (selected)
src/
index.ts public entry — re-exports the supported surface
client.ts connect() + app composer
auth.ts / oidc.ts native Cognito auth; dormant hosted-UI flow
me.ts identity + role derivation from cognito:groups
collection.ts doc.ts files.ts data primitives (logs/views via s3db)
functions.ts invoker.ts calls.ts server-function client side
server-runtime.ts the Node Lambda runtime (subpath export)
bundle-functions.ts deploy-side bundling of functions/*.ts (+ built-ins)
users.ts operators.ts roles-module.ts admin facades
email*.ts domains.ts secrets.ts identity.ts
contracts.ts shared shapes + renderProjectModule
conventions.ts paths.ts policy-templates.ts
raw.ts re-export raw @aws-sdk/client-s3Dev loop
From the workspace root:
npm install
npm run -ws build
npm test --workspace=@s3p/sdkOr per-package, from packages/sdk/:
npm run build # tsup → dist/
npm run dev # tsup --watch
npm test # vitest run
npm run typecheck
npm run lintAfter changing SDK sources, run npm run build and refresh the
consumer app — apps fetch the SDK as a plain ES module at runtime,
so no app rebuild is needed. After editing the React Email components
in emails/, re-run npm run build:emails.
Tests
Vitest suite covers auth flows, STS exchange, group → role derivation, collection/log behavior with mock S3, envelope encryption recipient-set permutations, function dispatch, email, and surface contract tests.
