Household tickets. Scan once, can't be reused.
This is the ticket example. Your organization issues tickets. Members read their own. Ushers work the check-in desk — they can scan you in, but they can't edit tickets. Once a ticket is scanned, it's used. Can't be scanned again. Need to reissue? The office can do that. Built for how humans actually are: tickets get lost, plans change, people show up late.
The permissions are the app. This is the whole data model — schema.ts, the first of the app's three files (its import line, not shown, brings these names in from "create-dillo/schema"):
// The schema embedded in the live worker today (span2/live-schemas filing, re-verified at deploy).
import { anyone, members, nobody, owner, relation, roles, schema, string, table } from '@29court/armadillo/schema';
// Services are public listings. A household is a team: the owner issues tickets,
// and members can read them. Ticket writes are closed, so nobody can edit a
// ticket through the record API. checkIn is the only way a ticket is checked in.
// Shifts belong to the trusted usher desk. Only that desk's owner can write them.
// An usher role can call checkIn and cannot read or write shifts or tickets.
export default schema({
Service: table({
name: string().min(1).max(120),
startsAt: string().datetime(),
}).permissions({
view: anyone,
edit: owner,
}),
Household: table({
householdId: relation.team(),
name: string().min(1).max(120),
}).permissions({
view: members,
edit: roles('owner'),
}),
Member: table({
householdId: relation.team(),
name: string().min(1).max(120),
}).permissions({
view: members,
edit: roles('owner'),
}),
Ticket: table({
householdId: relation.team(),
serviceId: relation('Service'),
guest: string().min(1).max(120),
code: string().min(16).max(80),
checkedInAt: string().datetime().optional().nullable(),
checkedInBy: relation.user().optional().nullable(),
}).permissions({
view: roles('owner', 'member'),
edit: nobody,
}),
Shift: table({
deskId: relation.team(),
serviceId: relation('Service'),
usherId: relation.user(),
}).permissions({
view: roles('owner'),
edit: roles('owner'),
}),
});