Snagset

Identity: guests, review links, and your own users

Nobody signs up to leave a comment. That is the point of the product, and it is also the thing that needs explaining, because "no account" and "no access control" are not the same sentence.

The three kinds of person

KindHow they got inNamed
guestTyped a name, or nothingBy what they typed
linkOpened a review link you sentBy the name on the link
verifiedYour own application vouched for themBy your user record

Plus you — the studio. That is a real account with a password, on the dashboard, and it is a completely separate system: different table, different credential, different origin, different lifetime. A reviewer credential can never become a dashboard credential, because there is no code path between them.

Who is allowed to comment

A per-site setting, and the default changes after a day:

write_modeWho can write
openAnyone who can load the page
link-requiredOnly somebody holding a review link
verified-onlyOnly users your application vouches for

A new site starts open for 24 hours, then falls back to link-required on its own. That window exists so the first pin needs no set-up — paste the tag, click, type — and it closes on its own so that a site you set up and forgot is not a public comment box a year later.

SNAGSET_WRITE_MODE_GRACE_HOURS changes the window. Set it to 0 for link-required from the first second.

The link is the access control. There is no separate permission screen, because a second system to keep in step with the links is a second system to get wrong.

A link carries:

  • A name, so comments are attributed without anyone typing one
  • What the holder may do — six permissions, of which three are on by default
  • An expiry — 14 days unless you say otherwise
  • Optionally a round, so the link lands on the round you meant
snagset link prj_… "Ana at Acme"
PermissionDefault
thread:create
comment:create
thread:resolve
thread:delete:own
thread:private
thread:assign

Revoking is immediate. It bumps the project's session epoch, so every session minted from that link stops verifying on its next request — including a tab that was already open, which is told it was signed out rather than quietly dropping to read-only.

An unknown permission is refused at creation, not stored and ignored. A permission that is silently dropped reads to the operator as "I granted it and it did nothing".

Sessions

A reviewer's credential is a short-lived signed token: 15 minutes sliding, 12 hours absolute. It lives in memory on the client and is exchanged again when it lapses. It is not a cookie, because the widget runs on somebody else's origin where a cookie would be both useless and dangerous.

The link itself lives only in the URL fragment — after the # — so it is never sent to your server, never lands in an access log, and never appears in a Referer header. It is taken out of the address bar on arrival.

Your own users (verified)

If your application already knows who is looking at the page, tell Snagset and skip the link entirely. Your server signs a short HMAC over the user's id and name with a shared secret; the widget presents it; a verified reviewer is created or matched.

That is the tier with the moderation bypass, and the only one — which is why an agent writing through MCP is never verified, whatever name its token carries. It posts as a guest, flagged as an agent, traceable to the token that made it.

What a reviewer can never do

  • See a site they were not given a link to
  • See a thread marked team-only
  • Reach the dashboard
  • Add a site, mint a link, or change a setting
  • Act on another project, even holding a valid credential for this one

Every one of those is checked on the server, per request. Absent CORS headers stop a browser on another origin from reading a response; they stop nothing else, which is a lesson this codebase learned by minting a review link with curl against its own port.