Toegang en identiteit
Breng identiteit mee. Bepaal toegang.
Section titled “Breng identiteit mee. Bepaal toegang.”De Worker verifieert kortlevende, ondertekende workspace-claims. Je applicatie implementeert de identity-adapter die het ingelogde subject en diens workspacerol bepaalt. Productie weigert de development-claimroute.
Elke actie van een model neemt één van drie beslissingen:
| Regel | Doel |
|---|---|
role(...) |
Vereist een minimumrol voordat SQL draait. |
filter(...) |
Past een leesregel toe binnen SQL-queries en counts. |
check(...) |
Beoordeelt een rij of schrijfactie met applicatielogica. |
board: model({ name: t.string(), ownerId: t.string().index(),}).access({ read: filter(({ auth }) => ({ ownerId: auth.sub })), update: check(({ auth, row }) => row?.ownerId === auth.sub), delete: role("moderator"),}),Defaults: lezen member, create/update editor, delete moderator.
filter(({ auth }) => where)is alleen voorread. Het wordt in de SQL van elke query gecompileerd die het model raakt, dus het gebruikt indexen, vult elke pagina en telt correct. Een array is OR; het filter van de caller ANDt ermee.check()werkt voor elke actie. Het ontvangt{ auth, row, data, changed }en geeft een boolean terug. Schrijfchecks draaien binnen de transactie naast de schrijfactie die ze bewaken. Bijreadscannen ze overeenkomende rijen tot de pagina vol is of de resultaatset uitgeput is. Counts passen dezelfde zichtbaarheidsregel toe.- Een regel die een fout gooit, weigert de toegang.
Geef SQL-filters de voorkeur voor leeszichtbaarheid, omdat een leescheck op basis van een functie na het ophalen draait.
Bestanden hebben eigen regels, in hetzelfde schema: zie Toegang tot bestanden.
Claims en identiteit
Section titled “Claims en identiteit”De engine verifieert een claim en geeft er nooit een uit. Implementeer twee methoden en de library serveert de claimroute, standaard met een rate limit:
import { defineTapeDB, WORKSPACE_ROLE_LEVELS } from "tapedb/worker";import schema from "./tapedb.schema";import { migrations } from "./tapedb/migrations.gen";
const tapedb = defineTapeDB({ schema, migrations, identity: { async getSubject(headers) { const session = await auth.api.getSession({ headers }); return session ? { sub: session.user.id } : null; }, async getRole(workspaceId, sub) { const member = await memberships.get(workspaceId, sub); return member ? WORKSPACE_ROLE_LEVELS[member.role] : null; }, // Optioneel; dit zijn de standaardwaarden. limits: { ip: { requests: 300, perSeconds: 60 }, subject: { requests: 30, perSeconds: 60 }, }, },});
export const WorkspaceRoom = tapedb.WorkspaceRoom;export default tapedb.worker;POST /_tapedb/claim geeft { token, workspaceId, roleLevel, expiresAt, expiresIn } terug. De route houdt een token bucket bij per IP van de aanroeper, gecontroleerd vóór het opzoeken van de sessie, en een per subject, gecontroleerd daarna. De buckets staan in Durable Objects, dus een telling is exact en globaal, en ze bewaren HMAC’s van het IP en het subject, nooit de waarden zelf. Boven een limiet antwoordt de route met 429 rate_limited en Retry-After; zet een limiet op false, of limits: false, om hem uit te zetten, en voeg allowRequest(key) toe voor een eigen controle, die na de IP-limiet draait met key = claim:<IP van de aanroeper>. Is de limiter onbereikbaar, dan gaan claims door en logt de Worker claim_limit_unavailable. De client stuurt het als Authorization: Bearer <token> en verlengt het bij een reconnect. In development zet tapedb dev een dev-claimroute aan, zodat de client helemaal geen sessie nodig heeft; ENVIRONMENT=production weigert die route, en tapedb deploy weigert de flag te versturen.
Schemawijzigingen en hun SQL-migraties staan in Migraties.