Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cryguy/hashboard/llms.txt

Use this file to discover all available pages before exploring further.

Access in Hashboard is governed by a four-level visibility system that sits on boards, cards, and standalone documents. Visibility determines both who can read and who can write, and it cascades downward through the resource hierarchy so you rarely need to set it more than once. The design is intentionally simple — no role matrices, no permission groups — but a few specific rules matter a great deal for correctness.

The Four Levels

LevelWho can readWho can write
privateCreator’s household + card assigneesCreator’s household only
link-readAnyone with the URLCreator’s household only
link-writeAnyone with the URLAnyone with the URL
publicAll account holders + unauthenticated visitorsCreator’s household only
Household means the creator principal and any agents that principal owns. Agents act under their owner’s authorization (principals.owner_id) and therefore share the creator’s write access.
The URL is the capability for link-read and link-write. Resource IDs are random UUIDs — Google Docs-style, with no rotatable tokens. Knowing the URL is sufficient proof of access. This means link-shared items are never listed or returned in search results, feeds, or backlink queries for principals who don’t already hold the resource. Listing an ID would be the same as sharing it.

Cascade Rules

Visibility cascades downward only, as a union:
1

Board → Cards

Placing a card on a board is an act of publication to that board’s audience. The board’s visibility is granted to each card at that level. A card on a link-read board is readable by anyone with the board URL — you do not need the card’s URL separately.
2

Card → Description doc and linked docs

The card’s effective visibility (its own setting, or the board’s if more permissive) cascades to the card’s description document and to any standalone docs linked via card_doc_links.
3

No upward flow

A standalone doc filed under a board (docs.board_id) does not inherit the board’s visibility. docs.board_id is a filing facet — it groups documents in the UI — not a cascade edge. A private standalone doc stays private even when filed under a public board.
Cascade is additive, never restrictive. A card set to public on a private board is accessible to everyone — the card’s own setting wins. If you want board-level access control to be authoritative, set cards to private and let the board’s visibility be the sole grant.

Private Boards and Card Assignees

A private board is visible only to the creator’s household, with one deliberate exception: card assignees can read the card they are assigned to, even if the containing board is private. Assignment is a sharing grant that is durable — it survives visibility changes on the board and on the card. Assignees have read access, not write access; write access remains with the creator’s household. This is why assigning is restricted to the card’s own circle (the creator’s household plus existing assignees). Allowing arbitrary principals to assign others would be a privilege escalation: you could share a private card with anyone by assigning them.

Anonymous Visitors and the Guest Principal

Unauthenticated visitors act as the seeded Guest principal (GUEST_PRINCIPAL_ID). Guest is a real principals row — it exists so that anonymous edits under link-write can satisfy the NOT NULL attribution columns in doc_revisions, activity, and comments without any schema special-casing. Guest ownership is deliberately empty:
  • Guest owns nothing — household() returns [], so no creator or household grants apply.
  • Guest cannot authenticate, be assigned to a card, or hold an instance role.
  • Guest-created cards on a board are administrable by that board’s household.
The complete set of paths reachable without authentication is the allowlist in lib/server/anonymous.ts. That list includes the three resource page patterns (/boards/:id, /cards/:id, /docs/:id and their __data.json SvelteKit variants), attachment downloads at /attachments/:id, and the exact API verbs that link-write visitors need. Widening that allowlist requires a review of the full service chain for each path added. For anything outside the allowlist, an unauthenticated request receives:
  • /api/* and /mcp*401 { "error": "unauthorized" } (JSON)
  • Page routes303 redirect to /auth/login
  • Rendition requests (.md suffix or Accept: text/markdown) → plain 401 text, never a login redirect — rendition consumers are machines
Responses are always 401, never 404 for resources that exist but are not accessible. Existence is never confirmed to an anonymous probe.

Agents and Authorization

Agents authenticate with bearer tokens (Authorization: Bearer hb_…) and act with the authority of their owning human. Authorization checks resolve through principals.owner_id: an agent whose owner is the board creator has the same write access the owner has. Attribution in activity feeds, comments, and revisions remains the agent’s own identity, not the owner’s — edits made by an agent are legible as the agent’s work.
An agent is a way a human uses the platform. The human is responsible for the agent’s actions, and the agent needs the human’s full access surface to be useful. Giving agents a reduced subset would mean constantly re-granting resources or building an intermediate permission layer. The current model keeps the grant simple (owner = agent) and keeps attribution honest (every action names the agent, not the human).

Visibility in the Schema

The visibility column appears on boards, cards, and docs. The enum is public | link-read | link-write | private. There is no CHECK constraint enforcing the enum in SQLite — values are service-validated — and the SQL DEFAULT is the vestigial value 'public' (changing a column default requires a table rebuild in SQLite; services write 'private' explicitly on every insert instead). Card docs hold a NULL visibility — enforced by the docs_visibility_kind_ck CHECK — and always inherit from their owning card. This is an invariant, not a convention: the two can never disagree.

Build docs developers (and LLMs) love