Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/webhood-io/webhood/llms.txt

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

Every Webhood API endpoint requires a valid credential. There are no public or anonymous routes. The system supports two credential types — interactive user sessions and long-lived API tokens — and enforces role-based access so that each credential class can only reach the routes appropriate for its purpose.

User Authentication

User accounts are stored in PocketBase’s users collection. Authentication is performed by posting an email/password pair to the standard PocketBase auth endpoint:
POST /api/collections/users/auth-with-password
A successful response includes a JWT that must be passed as a Bearer token on all subsequent requests. The core UI uses the official PocketBase JavaScript client to handle this flow; on a successful login the token is stored in localStorage via pb.authStore.
// src/core/hooks/use-api.ts
const authData = await pb
  .collection("users")
  .authWithPassword(email, password)
Outbound API calls from the UI attach the stored token automatically:
headers: {
  Authorization: `Bearer ${pb.authStore.token}`,
}

Example: Obtaining a Token with curl

curl -X POST http://localhost:8000/api/collections/users/auth-with-password \
  -H 'Content-Type: application/json' \
  -d '{"identity": "user@example.com", "password": "yourpassword"}'
A successful response returns a token field and a record object containing the user’s details including their role.
Self-registration is disabled by default (SELF_REGISTER=false in the compose environment). New user accounts must be created by a PocketBase admin or by an admin-role user through the Webhood UI Settings panel.

API Token Authentication

API tokens provide programmatic access without requiring an interactive login. They are stored in the api_tokens collection and are created in one of two ways:
  • Settings → Accounts in the Webhood UI (for regular API access tokens)
  • PocketBase admin panel for direct record management
To authenticate with an API token, pass it in the Authorization header exactly as you would a user JWT:
curl -X GET http://localhost:8000/api/beta/v1/scans \
  -H 'Authorization: Bearer <your-api-token>'
API tokens are valid for one year. This duration is defined as a constant in the backend:
// src/backend/src/webhood/config.go
const (
    ScannerAuthTokenValidDuration = 60 * 60 * 24 * 365 // 1 year
)
The backend’s middleware accepts credentials from both the users and api_tokens collections on API routes:
// src/backend/src/webhood/apiroutes.go
apis.RequireRecordAuth("users", "api_tokens")

Scanner Authentication

The scanner uses a special API token generated through the admin API. This token is issued per scanner record and is distinct from user-created API tokens. To generate one, an admin-role user calls:
POST /api/beta/admin/scanner/:id/token
This route is protected by WebhoodAdminApiMiddleware, which requires both a valid users credential and the admin role. The generated token is then placed in the SCANNER_TOKEN environment variable of the scanner container.
# docker-compose.yml
scanner:
  environment:
    SCANNER_TOKEN: ${SCANNER_TOKEN}
The scanner’s token carries the scanner role. This role is intentionally narrow: it permits the scanner to pick up queued scans and post results, but it cannot call any admin routes.

Role-Based Access Control

The RequireCustomRoleAuth middleware enforces role checks on protected routes. After standard PocketBase authentication succeeds, it reads the role field from the authenticated record and compares it against the role required by the route:
// src/backend/src/webhood/middleware.go
func RequireCustomRoleAuth(roleName string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            // Allow PocketBase admin accounts to pass unconditionally
            admin, _ := c.Get("admin").(*models.Admin)
            if admin != nil {
                return next(c)
            }
            // Otherwise require a matching role on the auth record
            record, _ := c.Get("authRecord").(*models.Record)
            if record == nil {
                return apis.NewUnauthorizedError("The request requires valid authorization token to be set.", nil)
            }
            if record.Get("role").(string) != roleName {
                return apis.NewUnauthorizedError("The request requires valid role authorization token to be set.", nil)
            }
            return next(c)
        }
    }
}
The middleware is applied in two different configurations depending on the route group:
Route groupCollections acceptedRole required
/api/beta/scans, /api/v1/scans (scanner routes)users, api_tokensscanner
/api/ui/scans (UI routes)users only(any authenticated user)
/api/beta/admin/*users onlyadmin
PocketBase super-admins (created via the /_/ admin panel) bypass the role check entirely and have unrestricted access to all routes.

Build docs developers (and LLMs) love