Permissions inside the query
Access checks are part of the database query, not a filter applied to a wider result. There is no over-broad set to forget to narrow.
Nine kinds of record, and only what you may open.
One search covers organizations, projects, documents, tasks, playbooks, companies, forms, automations and dashboards, together or one at a time. Membership checks are joined into the query itself, so a row you may not see is never fetched in the first place. Results cap at twenty, so the search box cannot become a load problem.
Product example
This is the same visual component used by the authenticated app, rendered with deterministic sample data.
Results include only the organizations, projects and documents you can already open.
Inert sample data · no network access
3 results · “pipeline”
↑↓to move↵to open
Preview ready
Highlights
Access checks are part of the database query, not a filter applied to a wider result. There is no over-broad set to forget to narrow.
A query is 1 to 100 characters, results cap at 20 globally ranked matches, and wildcard characters are escaped before any database work starts.
Search one registered type, a named group, or any combination. The registry records what is searchable, by whom, and with what limits.
Without a shared foundation, every team rebuilds these edge cases, differently.
Search that fetches broadly and filters afterwards will eventually miss a path. Joining the membership check into the query means the rows were never fetched, so there is nothing to leak.
Without caps, one very long query or one enormous result set turns our own search box into the thing that slows everything down. Hard limits on length and results prevent that.
When nothing records what is searchable and under which permission, every new feature reinvents search and re-argues access from scratch. One registry answers it once.
The mechanism end to end, as implemented in the repository.
Input is validated first
Query length, requested types and groups, filters, sort and result limit are all checked, and wildcard characters are escaped, before any database work begins.
The server picks the scope
Your active organization and project come from the session. The browser never decides whose records are searched.
Permissions join the query
Membership predicates are joined into the search itself, so candidate rows are narrowed before matching and ranking run.
Only selected lanes run
Groups and explicit types resolve to one canonical set. Only those authorized lanes execute, and their results receive a single global relevance order.
Engineering guarantees
Finding something is not permission to open it. Every feature checks access again when you open or edit the result. Meetings, decisions, objectives, campaigns and finance entries are not indexed today; companies and playbooks are the Growth Kit records search reaches.
The boundary above is code, not a claim. These files carry the contract:
src/db/postgres-search.tssrc/registries/search-indexes.tssrc/features/search/targets.tssrc/features/search/server.tsThese capabilities share contracts and boundaries with this one.
The docs describe the same contracts this page demonstrates.