stash
This commit is contained in:
parent
c345372382
commit
395a9b156a
9 changed files with 1434 additions and 0 deletions
131
docs/design-in-progress/items-labels.md
Normal file
131
docs/design-in-progress/items-labels.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# Item Handles & Physical Labels (Design in Progress)
|
||||
|
||||
Status: not implemented. This document collects the problem, goals, and open design questions for
|
||||
giving inventory items stable identifiers and physical (scannable) labels. Nothing here is
|
||||
settled.
|
||||
|
||||
## Problem
|
||||
|
||||
As described in federation.md's "Unique Handles" section, an item today is identified only by a
|
||||
local id scoped to its owner, it isn't given an explicit, portable handle the way a tag, property,
|
||||
or category is. That's fine as long as the only thing ever addressing an item is the owning user's
|
||||
own signed API traffic. It stops being fine the moment something *outside* that loop needs to
|
||||
refer to the item:
|
||||
|
||||
- A friend who borrowed a physical tool has no way to look it up other than finding it in the
|
||||
owner's shared inventory list by eye.
|
||||
There's nothing you could put on a sticker.
|
||||
- If items ever need to be referenced from outside their owner's own requests (a group's shared
|
||||
view, a lending record, a printed label), there's currently no stable identifier to reference
|
||||
that's meaningful outside the owner's own account.
|
||||
- A local database id isn't something we'd want to expose or rely on externally: it's an
|
||||
implementation detail of one backend's storage, not a handle with the same guarantees
|
||||
(uniqueness, meaning, longevity) the rest of the federation model gives every other kind of
|
||||
entity.
|
||||
|
||||
Put simply: every other kind of thing in Toolshed (users, tags, properties, categories) has a
|
||||
handle that means something outside of one database. Items don't, and physical labeling is the
|
||||
clearest case where that gap actually matters.
|
||||
|
||||
## Goals
|
||||
|
||||
- Give an item a handle that's meaningful and resolvable outside its owner's own account, without
|
||||
requiring items to become shared/reusable entities the way tags are (an item is still owned by
|
||||
exactly one person; see the "Items" subsection of federation.md for why that keeps things
|
||||
simple).
|
||||
- Support a physical label (QR code, barcode, or similar) that can be printed and stuck on a real
|
||||
object, such that scanning it gets you to the right item on the right backend.
|
||||
- Make the label survive the normal life of a physical object: it gets lent out, comes back,
|
||||
maybe changes which storage location it lives in, all without needing a new label printed.
|
||||
|
||||
## Non-goals (for now)
|
||||
|
||||
- Turning items into shareable/reusable entities across owners (that's what tags/categories are
|
||||
for; see [tags.md](tags.md)). An item handle identifies *this specific person's specific thing*,
|
||||
not a class of thing.
|
||||
- Solving inventory tracking/auditing (check-in/check-out logs) as a whole system; that can build
|
||||
on top of a stable item handle once one exists, but isn't the same problem.
|
||||
|
||||
## Open design questions
|
||||
|
||||
**What does the handle look like?**
|
||||
The natural extension of the existing scheme is owner handle + local id, that's enough to be
|
||||
globally unique (no two users share a handle, and ids are already unique within one user's
|
||||
inventory) without inventing a new namespace. Worth deciding whether the id should be the existing
|
||||
internal database id (simple, but leaks a little implementation detail and a rough count of
|
||||
someone's inventory) or a separate opaque id generated for exactly this purpose (see the
|
||||
unguessability question below).
|
||||
|
||||
Two distinct formats are needed, because "an item handle" is used in two different situations:
|
||||
|
||||
- *A compact handle, for use where context already makes clear it's a Toolshed item.* Inside the
|
||||
app, in exports, in logs, anywhere the reader already knows they're looking at Toolshed data,
|
||||
the handle doesn't need to spell that out or be openable on its own. This can be as short as
|
||||
`user@domain.tld:id`, the item's owner handle with `:id` appended, mirroring how a tag/category
|
||||
handle already appends `:name` after its origin (see federation.md's Unique Handles section).
|
||||
No new delimiter concept, just the same pattern applied to items.
|
||||
|
||||
- *A self-contained URL, for use with no context at all.* A physical label, a link shared outside
|
||||
the app, has to work without the reader already knowing what it is or which server it belongs
|
||||
to, so it needs to open directly to the right frontend, resolve the right backend, and land on
|
||||
the right item. That means it has to encode the same information (owner handle + item id) as a
|
||||
full URL, e.g. `https://toolshed.webdomain.tld/i/alice@example.com/42`, note the owner's handle
|
||||
can be embedded in a path segment as-is (`@` doesn't need escaping in a URL path), which keeps it
|
||||
one segment shorter than splitting the handle back into `domain/user`, and means the handle is
|
||||
visible unmodified inside the link rather than reassembled from separate parts. If the owner is
|
||||
ever a group rather than a user (see groups.md), its handle carries a leading `#`, which does need
|
||||
the `+`-for-`#` substitution described in [handles-and-shortids.md](../handles-and-shortids.md)'s
|
||||
Handle syntax section before it can sit in a path segment, e.g.
|
||||
`https://toolshed.webdomain.tld/i/+climbing@example.com/42`. The frontend host
|
||||
in this URL (`toolshed.webdomain.tld`) doesn't have to be, and generally won't be, the backend
|
||||
authoritative for `example.com`, any frontend can resolve any handle (see federation.md's Servers
|
||||
subsection), so this is just whichever frontend happens to be handling the link, not part of the
|
||||
item's identity.
|
||||
|
||||
Both formats should stay as short as the encoded information allows, this matters most for the URL
|
||||
form, since it's the one that ends up in a QR code or printed label where physical size is a real
|
||||
constraint (see the labels goal above).
|
||||
|
||||
**How does this fit with the frontend's existing routes?**
|
||||
There's already a `/inventory/shared/:user/:id` route (`InventoryDetailForeign`), but today
|
||||
`:user` is just a bare username with no domain, i.e. it only works for a friend on the viewer's own
|
||||
domain, and the view itself doesn't yet do anything domain-aware with that param. A resolvable
|
||||
global handle needs the full `user@domain.tld` and a lookup step this route doesn't have yet. Two
|
||||
ways to reconcile that: extend the existing route to take a full handle in the `:user` segment
|
||||
(`/inventory/shared/alice@example.com/42`, no new route shape needed, just a richer meaning for the
|
||||
param it already has), or treat the short `/i/...` URL as a dedicated, minimal entry point whose
|
||||
only job is to resolve a handle and then hand off into whatever the richer in-app view ends up
|
||||
being. The two aren't mutually exclusive: the short form is what needs to be small enough to print,
|
||||
the in-app route doesn't have the same constraint and can stay more descriptive.
|
||||
|
||||
**What does scanning a label actually do?**
|
||||
Probably: the label encodes a URL or handle-like string; scanning it opens the frontend, which
|
||||
resolves the owner's domain the same way it resolves any other handle (see federation.md), and
|
||||
lands on that item. This reuses the discovery mechanism that already exists for logging in as a
|
||||
handle, rather than inventing a second one.
|
||||
|
||||
**Does resolving a label require authorization?**
|
||||
An item's availability policy already controls who can see it (owner-only if private, friends if
|
||||
shared, etc.). A label should presumably respect the same policy rather than being a backdoor that
|
||||
makes a private item visible to literally anyone who finds the physical object and scans its code.
|
||||
That means resolving a label isn't a free public lookup, it goes through the same friend/signature
|
||||
checks as everything else, which has UX implications (an anonymous finder of a lost tool can't
|
||||
necessarily see who it belongs to).
|
||||
|
||||
**Does the label need to be opaque/unguessable?**
|
||||
If item ids are small sequential integers, a label built from a guessable id lets anyone enumerate
|
||||
a user's items by scanning or guessing nearby numbers, even if each individual lookup is
|
||||
authorization-checked. Probably wants some amount of unguessability even before authorization is
|
||||
considered, as a defense-in-depth measure.
|
||||
|
||||
**What survives item changes?**
|
||||
Storage location, availability policy, name, and description can all change over the life of an
|
||||
object without it becoming a "different" item. The label should point at the handle, not at any of
|
||||
that mutable data, so none of those changes require a new label. The one thing that probably *does*
|
||||
need a decision is deletion: does a handle ever get reused, or is it retired for good once an item
|
||||
is deleted (retiring seems safer, avoids an old label resolving to an unrelated new item later)?
|
||||
|
||||
**Relationship to lending/borrowing.**
|
||||
A scannable label is the obvious hook for a future "mark as borrowed / returned" flow. Not solving
|
||||
that now, but the handle scheme chosen here should be able to carry that later without a redesign,
|
||||
i.e. it should be able to identify the item independent of who currently physically has it.
|
||||
Loading…
Add table
Add a link
Reference in a new issue