Global search
across every engagement, finding, and client
Press Cmd+K (or Ctrl+K) inside the dashboard. Type two characters and SecPortal returns clients, engagements, and findings that match across the entire workspace. Results are RBAC-aware, debounced, and deep-link to the source record without leaving the keyboard.
No credit card required. Free plan available forever.
Two characters, one keystroke, the right record opens
Multi-tenant security workspaces grow quickly. A workspace running a real programme carries dozens of clients, hundreds of engagements, and tens of thousands of findings within a year. The cost of locating the right record through nested navigation adds up across every lookup, every triage handoff, every status call, and every audit walk. Programmes that paper over the cost with bookmarks and shared spreadsheet links lose the freshness that the engagement record provides; programmes that build complicated filter chains lose the speed.
SecPortal pairs the workspace dashboard with a Cmd+K palette that searches clients, engagements, and findings across the entire tenant. Type two characters and the response groups matches by type, deep-links to the source record, and respects the same RBAC rules that team management applies to the rest of the platform. The palette is a navigation surface, not a replacement for the queue pages or the activity feed; the design keeps it short enough to use without thinking.
What the palette indexes
The search runs against the operational entities that an operator most often needs to reach by name. The list below is what the search endpoint matches today; document content, comment text, scan output, and AI report bodies are intentional gaps and are described in the boundary section further down.
Clients
Search by company name or contact email. Only the workspace operator role (the consultant role inside the platform) sees client matches; non-consultant roles never receive the cross-client lookup, since the role check runs at the search API boundary before any cross-tenant query is built. Each match deep-links to the client detail page where engagements, findings, and invoices live in one view.
Engagements
Search engagement titles across every client in the workspace. The match opens the engagement page directly, so a security operator can land on the right scope, status, and finding queue from a two-character query rather than walking the client list, picking the engagement, and finally arriving at the work.
Findings
Search every finding title in the workspace, scoped to the tenant rather than to a single engagement. The match opens the parent engagement scoped to the finding, so retest notes, remediation status, severity, and the linked evidence are one click from the search result.
Tenant isolation, RBAC, and authentication boundary
Search inherits the same multi-tenant model as the rest of the platform. The workspace_id filter and the role check both run on every request, so the palette cannot accidentally surface cross-tenant matches and cannot leak rows scoped to a role the user no longer holds. The properties below come straight from the search route, not from a UI promise.
- Every query is scoped to the workspace_id derived from the authenticated profile. A workspace operator cannot return matches from another tenant even if a query string would otherwise hit a similarly named engagement on a different workspace; cross-tenant data isolation is enforced at the query boundary, not at the UI layer.
- The role check runs at the API boundary. Workspace operators (the consultant role inside the platform) get the full surface across clients, engagements, and findings. Non-consultant roles never see the client lookup at all, because the search endpoint branches on the role before the clients query runs, so the search result set itself respects the role boundary rather than relying on the redirect target to enforce it.
- The auth check runs first on every request. An expired session returns a 401 before any query is run, so a stale browser tab cannot leak partial results. The search endpoint never short-circuits the auth verification for caching or for performance.
- Search responses carry a private, max-age=10 cache header so a browser tab that issues several keystrokes in quick succession can reuse a fresh response, but a CDN or shared cache cannot store a result set that is scoped to one user inside one tenant.
- The dashboard MFA enforcement applies to the search surface for free. AAL2 session promotion via the multi-factor middleware gates the dashboard before the search input is reachable, so the search inherits the second-factor requirement rather than introducing a parallel path.
How the palette behaves under the keyboard
The palette runs in the browser as a focused dialog with a backdrop, a search input, and a grouped result list. The interaction defaults below are the behaviour the platform ships with; users do not configure them, so the surface stays consistent across roles and across sessions.
Cmd+K and Ctrl+K open the palette
A keyboard shortcut listener on the document opens the palette from anywhere inside the workspace dashboard. The same handler closes the palette if the shortcut is pressed again, so the surface lives behind muscle memory rather than behind a deliberate menu walk.
Two-character minimum query
Queries shorter than two characters return an empty result set without hitting the database. The minimum applies in both directions: a one-character query is treated as not yet specific enough to surface useful matches, and the palette displays a hint to type at least two characters before the request fires.
Three-hundred millisecond debounce
Each keystroke restarts a three-hundred millisecond timer. The fetch only fires once typing pauses, so a rapid query string entry does not blast a sequence of partial-prefix queries at the search endpoint. The debounce keeps the network surface modest without sacrificing perceived responsiveness.
Five matches per type, fifteen total ceiling
Each entity type returns up to five matches. The total result list never exceeds fifteen rows, which is short enough to scan with one eye movement. The cap is deliberate: a global search palette that returns hundreds of rows degrades into a table that the user has to filter again, which is exactly the failure mode the palette is meant to avoid.
Keyboard navigation across the result list
Arrow keys move the selection up and down across the grouped result list, Enter opens the highlighted match, and Escape closes the palette. The selected row also follows mouse hover, so the keyboard and the pointer share one selection state rather than fighting for cursor position.
Direct deep-link on selection
Selecting a result navigates straight to the source record. Clients open at the client detail page; engagements open at the engagement detail page; findings open the parent engagement scoped to the finding so the right severity, evidence, status, and remediation context is the first thing the operator sees.
How the search endpoint is shaped
The query plan is small and inspectable. Five properties below describe how the API boundary translates a user query into a result set, so the same shape can be reasoned about during a security review or a procurement walk-through.
- The query runs three searches in parallel: an ILIKE on the clients table for company name and contact email; an ILIKE on the engagements table for title; an ILIKE on the findings table for title. Promise.all keeps the three round trips inside one wall-clock window so the user does not wait three serial query latencies.
- Pattern matching uses ILIKE with %query% wrapping, so a substring anywhere in the indexed field surfaces a match. The match is case-insensitive and accent-folded by Postgres at the column collation level, which keeps the search forgiving for security teams who type quickly.
- Each per-type query is scoped with a workspace_id equality filter before the ILIKE is applied. The filter ordering guarantees that even if the matching pattern would otherwise match rows in another tenant, the workspace clause runs first and short-circuits the cross-tenant rows out of the candidate set.
- Each per-type query is limited to five rows. The limit keeps the response size predictable and keeps the response time bounded under realistic workspace sizes, where the indexed columns are short text fields and the workload is interactive rather than analytical.
- The grouped response carries a type label and a deep-link href on every row. The router-level navigation is computed from the row metadata, so the palette does not embed knowledge about workspace URL shape and does not fall out of sync when route handlers move.
Where the palette earns its keep
The lookups below are the everyday cases the palette is built for. None of them are novel; the difference is that they all collapse from a multi-step navigation walk to a single keystroke pattern.
Hand a finding off without opening five tabs
A scanner module surfaces a critical finding. The named owner gets a notification, opens the dashboard, presses Cmd+K, types three characters of the finding title, and lands on the engagement page scoped to the finding. The remediation queue is one keystroke from any other view in the workspace.
Pull up a client during a status call
A client lead joins a status call without warning. The workspace operator presses Cmd+K, types two characters of the client company name, and the client detail page opens with the engagements, the open findings count, the invoice state, and the activity feed visible at once. The lookup never touches the client list page.
Find a regression on the same finding title
A retest closes a finding and a follow-up scan reopens it. A reviewer typing two characters of the finding title sees both the originally created record and the reopened record on the search result list, scoped to the workspace. The result list is the entry point into the timeline that the activity log captures in detail.
Locate an engagement by title fragment
A security operator hands an engagement to a colleague. The colleague joins the workspace, presses Cmd+K, types the project codename, and the engagement opens directly. There is no need to walk the client list, identify the engagement among siblings, and click through to the right detail view.
Boundaries: what global search is and what it is not
Naming the boundary up front keeps the palette focused on its job and points teams at the right tool when they reach the edge.
- Search hits clients, engagements, and findings. It does not return rows from documents, comments, messages, invoices, scans, scan jobs, scan findings, or activity log entries. Document content discovery and comment thread discovery are intentional gaps; the palette is a navigation surface rather than a full-text index across the whole platform.
- Search runs against the title and the indexed name fields. It does not run against finding descriptions, finding remediation steps, finding evidence text, engagement scope text, or AI report bodies. A query that needs to match content inside a finding description has to open the engagement and use the in-engagement filter; the palette is for fast jumps, not for content excavation.
- Search is text-pattern matching, not vector search or semantic search. A query for "broken access" returns finding titles that contain the substring; it does not surface a finding titled "improper authorisation on POST /admin" unless the title actually contains the word fragment. Semantic search would change the contract from predictable to probabilistic.
- Search is scoped to one workspace at a time. There is no cross-workspace federation: a security consultancy with multiple workspaces switches workspace and re-searches rather than seeing pooled results. The single-tenant scope keeps the access boundary simple and matches the rest of the platform model.
- Search results live for the keystroke. There is no saved-search feature, no smart filter, and no per-user search history persistence beyond the in-tab state. Users who need a recurring query (open critical findings, overdue engagements) read those views from the queue pages and the activity feed, not from the palette.
Questions an enterprise reviewer should be able to answer in five minutes
Procurement and security architecture reviews ask the same questions about any platform that exposes a global query surface. The answers below come from the search route handler and the multi-factor middleware rather than from a dashboard widget.
- When a workspace operator searches for a string that matches an engagement title in their tenant and an engagement title in another tenant, do the cross-tenant results appear? The workspace_id filter forecloses that case before the ILIKE runs.
- When a non-consultant role hits the search endpoint, do they see clients in the result list? No. The role check runs on every request, so the clients query branches off entirely for non-consultant roles even before the workspace_id filter is applied to the query plan.
- When a session expires mid-keystroke, does the search endpoint return a 401? Yes. The auth check runs before the workspace lookup, so a stale tab cannot leak data through a residual cookie.
- When a workspace member is demoted from the consultant role, do they still see clients in the result list? No. The role check runs on every request and the clients query branches off the role, so a non-consultant role never receives client matches even if their last query was successful.
- When the workspace contains tens of thousands of findings, does the palette stay responsive? The per-type limit of five and the workspace_id index make the latency a function of result-set selectivity rather than of total table size, so the palette stays interactive across realistic workspace sizes.
- When the request is replayed after a key rotation or a permission change, does the result set reflect the new permissions? Yes. The search endpoint reads the role from the live profile rather than from a cached token, so a permission change is visible on the next keystroke without a sign-out.
Use the palette where it shines, route around it where it does not
The palette earns its keep by being narrow and fast. Pushing the surface beyond its intended job degrades the speed without filling the gap properly, so the dos and do nots below describe the shape of disciplined daily use.
Do
Use the palette as the primary navigation across engagements and findings. Memorise the Cmd+K shortcut, type two or three characters of the title, and pick the result with arrow keys. The operating-cost saving across a working day on a busy workspace is real, even though every individual lookup is cheap.
Do
Use the palette during live calls. The lookup is fast enough to run mid-conversation without losing the thread; the deep-link lands on the engagement page so the screen-shared view shows scope, findings, and timeline at once instead of a folder hierarchy.
Do not
Treat the palette as a content search across descriptions, comments, or AI reports. Those fields are not indexed in the search query. For content-level discovery, open the engagement and use the in-engagement filter on the finding list, or read the activity feed directly.
Do not
Build automation that hits the search endpoint to enumerate workspace state. The endpoint is shaped for interactive UI use, with a fifteen-row ceiling, a short cache window, and a debounced front-end. Bulk reads belong on the bulk APIs and the activity feed export.
Where global search fits in the rest of the platform
The palette is the navigation layer over findings management, engagement management, and the client list. A finding match opens the engagement scoped to the finding so the named owner runs through remediation tracking and scanner result triage on the same record the palette landed them on, without an extra hop.
The history of every record the palette resolves to lives in the activity log, and the live signal layered over those events runs through notifications and alerts. The palette closes the loop between a notification that points to a record and the keyboard shortcut that opens it from any view.
For the buyer-side framing of who runs this discipline daily, see the audience pages for internal security teams, security operations leaders, and vulnerability management teams. Each one carries a different daily cadence over the same record, and the palette stays the same underneath.
Authentication for the dashboard surface that the palette lives inside is gated by multi-factor authentication, and the same RBAC tier that controls every other action on the workspace controls what the palette returns. The search palette is fast because it is small; it is small because the rest of the platform does the work the palette does not have to.
Stop hunting findings through nested folders
Cmd+K. Two characters. The right engagement, finding, or client lands in the result list. No filter chains, no breadcrumb walks.
No credit card required. Free plan available forever.