Documentation
¶
Overview ¶
Package api wires all HTTP handlers together into a chi router and applies cross-cutting middleware.
Index ¶
- Constants
- Variables
- func DecodeJSON(r *http.Request, dst any) error
- func HandleHealth(w http.ResponseWriter, _ *http.Request)
- func HandleHealthWithQueue(queueStatus string) http.HandlerFunc
- func HandleReady(w http.ResponseWriter, _ *http.Request)
- func Logging(next http.Handler) http.Handler
- func NewCORS(allowedOrigins []string) func(http.Handler) http.Handler
- func NewRouter(cfg RouterConfig) http.Handler
- func Recoverer(next http.Handler) http.Handler
- func RegisterDocsRoutes(r chi.Router)
- func RequireOrgAdmin() func(http.Handler) http.Handler
- func RequireOrgAdmin404() func(http.Handler) http.Handler
- func RequireSpaceInOrg(resolve SpaceOrgResolver) func(http.Handler) http.Handler
- func RequireSpaceReadable() func(http.Handler) http.Handler
- func RequireWriteFloor(c access.Capability) func(http.Handler) http.Handler
- func ResolveAccess(resolver *access.Resolver) func(http.Handler) http.Handler
- func ResolveShares(resolver *access.Resolver) func(http.Handler) http.Handler
- func SecurityHeaders(next http.Handler) http.Handler
- func WriteError(w http.ResponseWriter, r *http.Request, status int, code respond.ErrorCode, ...)
- func WriteJSON(w http.ResponseWriter, status int, v any)
- type RouterConfig
- type SpaceOrgResolver
- type SwaggerAddMemberRequest
- type SwaggerAssignRequest
- type SwaggerBoardColumn
- type SwaggerBoardConfig
- type SwaggerCommentResponse
- type SwaggerCompleteSprintRequest
- type SwaggerCreateCommentRequest
- type SwaggerCreateGrantRequest
- type SwaggerCreateItemRequest
- type SwaggerCreatePageRequest
- type SwaggerCreateRelationRequest
- type SwaggerCreateShareRequest
- type SwaggerCreateSpaceRequest
- type SwaggerCreateSprintRequest
- type SwaggerCreateTeamRequest
- type SwaggerCreateTicketRequest
- type SwaggerErrorDetail
- type SwaggerErrorResponse
- type SwaggerHealthResponse
- type SwaggerKanbanColumn
- type SwaggerLoginRequest
- type SwaggerLoginResponse
- type SwaggerLogoutResponse
- type SwaggerMessageResponse
- type SwaggerMovePageRequest
- type SwaggerMoveToBacklogRequest
- type SwaggerMoveToSprintRequest
- type SwaggerOrgResponse
- type SwaggerPatchTeamRequest
- type SwaggerPutMemberRequest
- type SwaggerRefreshRequest
- type SwaggerRefreshResponse
- type SwaggerRegisterRequest
- type SwaggerRequesterIdentity
- type SwaggerSprintAssignRequest
- type SwaggerStatusRequest
- type SwaggerTicketResponse
- type SwaggerTransitionRequest
- type SwaggerUpdateGrantRequest
- type SwaggerUpdateItemRequest
- type SwaggerUpdateOrgRequest
- type SwaggerUpdatePageRequest
- type SwaggerUpdatePortalRequest
- type SwaggerUpdateSpaceRequest
- type SwaggerUpdateSprintRequest
- type SwaggerUpdateTicketRequest
- type SwaggerUserResponse
Constants ¶
const ( CodeNotFound = respond.CodeNotFound CodeValidation = respond.CodeValidation CodeForbidden = respond.CodeForbidden CodeConflict = respond.CodeConflict CodeInternal = respond.CodeInternal CodeBadRequest = respond.CodeBadRequest CodeInvalidTransition = respond.CodeInvalidTransition )
Re-export error codes so existing code within the api package still compiles.
const ContentSecurityPolicy = "default-src 'self'; " +
"script-src 'self'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: blob: http: https:; " +
"font-src 'self' data:; " +
"connect-src 'self'; " +
"media-src 'self' blob:; " +
"object-src 'none'; " +
"base-uri 'self'; " +
"form-action 'self'; " +
"frame-ancestors 'none'"
ContentSecurityPolicy is the policy served on every response.
One global policy rather than a per-route one, on purpose: the SPA, the JSON API, the Swagger UI and the wiki render endpoint all sit on a single origin, and a policy that varies by path is a policy nobody can reason about. Every directive below is the strictest value that leaves a shipped feature working, and where it is not strict the reason is written down.
- script-src 'self' is the point of the whole header. No 'unsafe-inline', no 'unsafe-eval', no nonce, no hash. The built SPA carries no inline script — vite emits `<script type="module" crossorigin src="/assets/…">` and nothing else — and the Swagger UI page's initialiser was moved out to /api/docs/init.js so this directive could stay bare. Markup that gets past the wiki sanitiser still cannot execute.
- style-src needs 'unsafe-inline', and it is the one loosening here. Three independent sources need it and none can be nonced away: the Swagger UI page's literal <style> block, and at runtime the <style> elements tiptap (createStyleTag) and react-style-singleton inject into the head. React's own style={{…}} props are unaffected either way — React writes those through the CSSOM, which CSP does not govern. Styles cannot execute; the residual risk is defacement, not code.
- img-src is deliberately as wide as the wiki sanitiser's own URL policy. rehype-sanitize's default schema permits http and https `src`, converted legacy markdown pages carry external images, and attachments are fetched with a bearer token and handed to <img> as blob: URLs (fetchObjectURL in web/src/lib/api.ts). Narrowing it here would break shipped content from a second place while the sanitiser still permitted it — two policies disagreeing about one question. What decides which URLs reach a page is the sanitiser; this directive only has to not contradict it.
- font-src needs data: — the built stylesheet inlines Inter as url(data:font/woff…).
- media-src carries blob: for the same reason img-src does: every attachment reaches the browser through fetchObjectURL, and an audio or video attachment is that same code path with a different element. It is the one directive here that is not exercised by anything shipped today — kept because the failure it would otherwise produce is a silent one, a dead player and a console line nobody is watching. There is deliberately no worker-src: nothing in this tree constructs a Worker, and a same-origin one would fall back to default-src and work anyway.
- connect-src 'self' matches the frontend's default API base of /api/v1. An operator who builds the SPA with an absolute VITE_API_BASE_URL is pointing it at another origin and must widen this — the same coupling AZIMUTHAL_ALLOWED_ORIGINS already carries.
- object-src 'none' and base-uri 'self' close the two classic bypasses a script-src alone leaves open: plugin content, and rewriting the document base so a relative script src resolves off-origin.
- frame-ancestors 'none' is the modern clickjacking control; the X-Frame-Options header beside it says the same for browsers that never learned the directive. Nothing in this product embeds itself in a frame.
Variables ¶
var RequestID = respond.RequestID
RequestID is middleware that assigns a unique request ID to each request.
var RequestIDFromContext = respond.RequestIDFromContext
RequestIDFromContext returns the request ID from the context, or empty string.
Functions ¶
func DecodeJSON ¶
DecodeJSON reads the request body into dst.
func HandleHealth ¶
func HandleHealth(w http.ResponseWriter, _ *http.Request)
HandleHealth responds to liveness probes with {"status":"ok","queue":"ok|disabled"}.
@Summary Liveness probe @Description Returns {"status":"ok"} when the server is running. Includes queue status. @Tags health @Produce json @Success 200 {object} healthResponse "Server is alive" @Router /health [get]
func HandleHealthWithQueue ¶ added in v0.1.15
func HandleHealthWithQueue(queueStatus string) http.HandlerFunc
HandleHealthWithQueue returns a handler that includes queue status in the health response.
func HandleReady ¶
func HandleReady(w http.ResponseWriter, _ *http.Request)
HandleReady responds to readiness probes with {"status":"ready"}.
@Summary Readiness probe @Description Returns {"status":"ready"} when the server is ready to accept traffic. @Tags health @Produce json @Success 200 {object} healthResponse "Server is ready" @Router /ready [get]
func NewCORS ¶ added in v0.1.15
NewCORS returns a CORS middleware that only echoes Access-Control-Allow-Origin when the request's Origin matches one of allowedOrigins. The wildcard "*" in allowedOrigins permits any origin and must only ever come from an operator setting AZIMUTHAL_ALLOWED_ORIGINS=* deliberately.
A nil or empty list emits no CORS headers at all, which is the default in every environment: the browser then enforces same-origin, which is what the SPA needs (it is served from this same binary in production and through Vite's server-side /api proxy in development, so neither is a cross-origin caller). Cross-origin access is a boot-time decision, never a runtime one.
This function is the only CORS middleware. A permissive `CORS` variant that echoed Access-Control-Allow-Origin: * unconditionally used to sit beside it and was selected whenever RouterConfig.AllowedOrigins was nil — a fail-open default that every test harness silently picked up. It was removed in the S5 security pass; do not reintroduce a "just for tests" permissive path.
func NewRouter ¶
func NewRouter(cfg RouterConfig) http.Handler
NewRouter builds the unified chi router with all routes and middleware.
func RegisterDocsRoutes ¶ added in v0.1.14
RegisterDocsRoutes adds API documentation routes to the router. GET /api/docs -> Swagger UI (interactive documentation) GET /api/docs/init.js -> the UI page's initialiser (see swaggerUIHTML) GET /api/docs/openapi.yaml -> raw OpenAPI 3.0 spec
func RequireOrgAdmin ¶ added in v0.3.1
RequireOrgAdmin rejects non-admin callers with 403. Used for org-level administrative mutations on member-visible resources (team management, workflow admin), where the resource's existence is already known to members.
func RequireOrgAdmin404 ¶ added in v0.3.2
RequireOrgAdmin404 rejects non-admin callers with 404 — never 403. The P2.5 administration surface (people, invites, matrix, audit log) does not exist as far as non-admins can tell, matching the §2.6 "no access → 404" convention for surfaces whose existence itself is privileged.
func RequireSpaceInOrg ¶ added in v0.2.0
func RequireSpaceInOrg(resolve SpaceOrgResolver) func(http.Handler) http.Handler
RequireSpaceInOrg enforces the single org+space scoping convention: a request to /orgs/{orgID}/spaces/{spaceID}/... only proceeds when the space exists AND belongs to that org — otherwise 404. Requests whose route has no spaceID parameter (org-level space list/create) pass through untouched.
func RequireSpaceReadable ¶ added in v0.3.1
RequireSpaceReadable 404s any {spaceID}-scoped request whose space is not in the caller's resolved readable set. Runs after RequireSpaceInOrg, so the space is already known to belong to the org — this guard adds the access decision. 404, never 403: unreadable spaces do not exist as far as the caller can tell (spec §2.6).
func RequireWriteFloor ¶ added in v0.3.1
RequireWriteFloor enforces the write floor on a space resource subtree: reads pass (the readable guard already ran), every mutating method needs at least the given capability. Handlers refine above the floor — the edit_own/edit_any split and agent-tier checks live with the entity.
func ResolveAccess ¶ added in v0.3.1
ResolveAccess is the per-request permission resolution middleware (spec §5, ADR-0007). Mounted once on the /orgs/{orgID} subtree, it resolves the caller's readable space set and per-space roles in a constant number of queries, caches the result on the request context, and 404s callers who are not members of the org — existence is never leaked.
The cache lives for exactly one request. Nothing is shared across requests, which is what makes grant revocation immediate.
func ResolveShares ¶ added in v0.3.3
ResolveShares resolves the caller's entity-share coverage once and caches it on the request context (spec §5 readable_entity_ids, ADR-0008). It is mounted ONLY on the share-authorised read subtree — space-scoped routes never pay for it, so the P2 per-request query budget on those routes is unchanged. Runs after ResolveAccess, which has already 404'd non-members; a resolution failure here fails closed with 500 rather than granting.
func SecurityHeaders ¶ added in v0.3.4
SecurityHeaders sets the response headers that hold for every route.
X-Content-Type-Options: nosniff stops a browser second-guessing a declared Content-Type and executing bytes as a type the server did not choose. The attachment and avatar serve paths each set it per-response already; setting it globally means a route added later inherits it rather than having to remember, and it reaches the routes that never set it — notably the wiki render endpoint, which returns user-authored content as text/html.
It is set before the handler runs, so a handler's own Set replaces it rather than duplicating it.
What it is NOT: a defence against a content type the server declares deliberately. nosniff constrains sniffing, not rendering — a response the server labels text/html is still rendered as HTML. That is exactly why the attachment serve path sniffs the stored bytes instead of relying on this header, and why adding this middleware would not on its own have closed that hole.
The other four headers arrived with the v0.4.1 trust patch. Until then this middleware set nosniff and nothing else: no CSP, so script that reached a rendered page ran with the origin's full authority; no frame controls, so the whole app could be framed; no referrer policy, so a full URL — which in this product carries org, space and entity ids — travelled to every cross-origin destination a user clicked through to.
Referrer-Policy is strict-origin-when-cross-origin: a same-origin navigation keeps the full path (the SPA is one origin, so nothing internal is lost), a cross-origin one sends the bare origin, and an HTTPS→HTTP downgrade sends nothing at all.
func WriteError ¶
func WriteError(w http.ResponseWriter, r *http.Request, status int, code respond.ErrorCode, msg string)
WriteError writes a structured JSON error response.
Types ¶
type RouterConfig ¶
type RouterConfig struct {
Authenticator *auth.Authenticator
AuthHandler *authapi.Handler
TicketHandler *ticketsapi.Handler
WikiHandler *wikiapi.Handler
ProjectHandler *projectsapi.Handler
SpaceHandler *spacesapi.Handler
CommentHandler *commentsapi.Handler
// RelationHandler serves the entity-generic relation satellite: one core,
// mounted per entity subtree (projects items, tickets, wiki pages) the way
// comments are — the from side of a relation comes from which route was
// hit. nil leaves every relation route unmounted, including the item ones
// that used to live inside ProjectHandler.Routes(); the harness wires it,
// and TestHarness_NoDarkDependencies fails on a nil.
RelationHandler *relationsapi.Handler
NotificationHandler *notificationsapi.Handler
WorkflowHandler *workflowsapi.Handler
TeamHandler *teamsapi.Handler
GrantHandler *grantsapi.Handler
// (manage_shares in-handler) and the /shared read family (share-
// authorised, not space-authorised). nil leaves both unmounted.
ShareHandler *sharesapi.Handler
// AttachmentHandler serves entity attachments (P3): the space-scoped
// upload/read family and the share-authorised read family. nil leaves
// both unmounted.
AttachmentHandler *attachmentsapi.Handler
// AdminHandler serves the P2.5 administration surface (people, matrix,
// audit viewer) behind RequireOrgAdmin404, plus the member-visible
// picker search. nil leaves the surface unmounted.
AdminHandler *adminapi.Handler
// InviteHandler serves the invite lifecycle: admin routes behind
// RequireOrgAdmin404 and the public token-authenticated acceptance
// routes. nil leaves both unmounted.
InviteHandler *invitesapi.Handler
// AvatarHandler serves user avatar upload (self + admin) and the
// org-member-readable serve endpoint. nil leaves the routes unmounted
// (e.g. when object storage is unavailable).
AvatarHandler *avatarapi.Handler
// ViewHandler serves saved views (P4, ADR-0009): the org-scoped /views
// family. Cross-container by nature, so it has no {spaceID} to hang off
// (ADR-0010).
ViewHandler *viewsapi.Handler
// PortalHandler serves the customer portal: the unauthenticated sign-in
// routes and the requester-authenticated request routes. nil leaves the
// whole surface unmounted, which is the correct default for a deployment
// that has not opted any space in.
PortalHandler *portalapi.Handler
// PortalService backs RequirePortalSession. It is separate from
// PortalHandler because the guard is middleware the ROUTER mounts, not
// something the handler can apply to itself — and mounting the handler
// without it would leave every requester route unauthenticated.
// TestHarness_PortalGuardIsMounted fails if the two ever disagree.
PortalService *portal.Service
// DashboardHandler serves dashboards and gadgets (P5, ADR-0009): the
// org-scoped /dashboards family. Org-scoped for the same reason /views is
// — a dashboard arranges gadgets that cross containers.
DashboardHandler *dashboardsapi.Handler
// SearchHandler serves cross-module search (P6, spec §5/§7): the
// org-scoped /search route. Org-scoped for the same reason /views is — a
// search spans containers by definition, so there is no {spaceID} to
// scope it to, and the per-viewer access set replaces the space guard.
SearchHandler *searchapi.Handler
SPAHandler http.Handler // serves the embedded frontend; nil disables SPA serving
// AllowedOrigins is the explicit CORS allow-list, and nil or empty is the
// safe default: no CORS headers are emitted and the browser enforces
// same-origin. Cross-origin callers are admitted only by an operator
// setting AZIMUTHAL_ALLOWED_ORIGINS at boot.
//
// This field used to fail open — nil selected a permissive middleware that
// echoed Access-Control-Allow-Origin: * on every response. Leave it unset
// and you now get the restrictive behaviour, not the permissive one.
AllowedOrigins []string
// QueueStatus is reported in the /health response: "ok", "disabled", or "error".
QueueStatus string
// SpaceOrgResolver backs the RequireSpaceInOrg middleware that enforces
// the single /orgs/{orgID}/spaces/{spaceID}/... scoping convention.
SpaceOrgResolver SpaceOrgResolver
// AccessResolver backs the per-request permission resolution middleware
// (spec §5). nil (routing-only unit tests) leaves the middleware
// unmounted; every real construction site wires one, and the capability
// guards fail closed without a resolution on the context.
AccessResolver *access.Resolver
}
RouterConfig holds all the dependencies needed to build the API router.
type SpaceOrgResolver ¶ added in v0.2.0
SpaceOrgResolver returns the org that owns a space, or an error when the space does not exist. Used by RequireSpaceInOrg.
type SwaggerAddMemberRequest ¶ added in v0.1.14
type SwaggerAddMemberRequest struct {
UserID uuid.UUID `json:"user_id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
Role string `json:"role" example:"member"`
}
SwaggerAddMemberRequest matches addMemberRequest in spaces handler.
type SwaggerAssignRequest ¶ added in v0.1.14
type SwaggerAssignRequest struct {
AssigneeID uuid.UUID `json:"assignee_id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
}
SwaggerAssignRequest matches assignRequest in tickets handler.
type SwaggerBoardColumn ¶ added in v0.3.3
type SwaggerBoardColumn struct {
ID uuid.UUID `json:"id" example:"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
SpaceID uuid.UUID `json:"space_id" example:"b2c3d4e5-f6a7-8901-bcde-f12345678901"`
Name string `json:"name" example:"In Progress"`
Position int `json:"position" example:"2"`
// WIPLimit is null when the column has no limit. Limits are advisory: the
// API never refuses a transition because a column is over its limit.
WIPLimit *int `json:"wip_limit" example:"3"`
Statuses []string `json:"statuses" example:"in_progress,in_review"`
CreatedAt string `json:"created_at" example:"2026-01-15T10:30:00Z"`
UpdatedAt string `json:"updated_at" example:"2026-01-15T10:30:00Z"`
}
SwaggerBoardColumn matches projects.BoardColumn.
type SwaggerBoardConfig ¶ added in v0.3.3
type SwaggerBoardConfig struct {
SpaceID uuid.UUID `json:"space_id" example:"b2c3d4e5-f6a7-8901-bcde-f12345678901"`
Columns []SwaggerBoardColumn `json:"columns"`
// Customized is false when the space has no stored configuration and these
// columns were derived from its workflow states.
Customized bool `json:"customized" example:"false"`
}
SwaggerBoardConfig matches projects.BoardConfig.
type SwaggerCommentResponse ¶ added in v0.1.14
type SwaggerCommentResponse struct {
ID uuid.UUID `json:"id" example:"c3d4e5f6-a7b8-9012-cdef-123456789012"`
ItemID string `json:"item_id,omitempty" example:"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
AuthorID uuid.UUID `json:"author_id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
AuthorName string `json:"author_name" example:"Admin User"`
Body string `json:"body" example:"This looks good, let's merge it."`
Content string `json:"content" example:"This looks good, let's merge it."`
CreatedAt string `json:"created_at" example:"2026-01-15T10:30:00Z"`
UpdatedAt string `json:"updated_at" example:"2026-01-15T10:30:00Z"`
}
SwaggerCommentResponse matches commentResponse in comments handler.
type SwaggerCompleteSprintRequest ¶ added in v0.3.3
type SwaggerCompleteSprintRequest struct {
NextSprintID *uuid.UUID `json:"next_sprint_id,omitempty" example:"c3d4e5f6-a7b8-9012-cdef-123456789012"`
}
SwaggerCompleteSprintRequest matches completeSprintRequest in projects handler. The body is optional; next_sprint_id names a carry-over sprint for incomplete items, or is omitted to return them to the backlog.
type SwaggerCreateCommentRequest ¶ added in v0.1.14
type SwaggerCreateCommentRequest struct {
Content string `json:"content" example:"This looks good, let's merge it."`
}
SwaggerCreateCommentRequest matches createCommentRequest in comments handler.
type SwaggerCreateGrantRequest ¶ added in v0.3.1
type SwaggerCreateGrantRequest struct {
SubjectType string `json:"subject_type" example:"team" enums:"user,team"`
SubjectID uuid.UUID `json:"subject_id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
Role string `json:"role" example:"viewer" enums:"viewer,contributor,agent,space_admin"`
}
SwaggerCreateGrantRequest matches createGrantRequest in grants handler.
type SwaggerCreateItemRequest ¶ added in v0.1.14
type SwaggerCreateItemRequest struct {
Title string `json:"title" example:"Implement search"`
Description string `json:"description" example:"Full-text search for items"`
Kind string `json:"kind" example:"task"`
Priority string `json:"priority" example:"medium"`
AssigneeID *uuid.UUID `json:"assignee_id,omitempty"`
SprintID *uuid.UUID `json:"sprint_id,omitempty"`
DueAt *time.Time `json:"due_at,omitempty"`
}
SwaggerCreateItemRequest matches createItemRequest in projects handler.
type SwaggerCreatePageRequest ¶ added in v0.1.14
type SwaggerCreatePageRequest struct {
Title string `json:"title" example:"Getting Started"`
Content string `json:"content" example:"# Welcome\nThis is a wiki page."`
ParentID *uuid.UUID `json:"parent_id,omitempty"`
Position int32 `json:"position" example:"0"`
}
SwaggerCreatePageRequest matches createPageRequest in wiki handler.
type SwaggerCreateRelationRequest ¶ added in v0.1.14
type SwaggerCreateRelationRequest struct {
ToID uuid.UUID `json:"to_id" example:"b2c3d4e5-f6a7-8901-bcde-f12345678901"`
ToType string `json:"to_type" example:"project_item"`
Kind string `json:"kind" example:"blocks"`
}
SwaggerCreateRelationRequest matches createRelationRequest in projects handler.
type SwaggerCreateShareRequest ¶ added in v0.3.3
type SwaggerCreateShareRequest struct {
}
SwaggerCreateShareRequest matches createShareRequest in shares handler.
type SwaggerCreateSpaceRequest ¶ added in v0.1.14
type SwaggerCreateSpaceRequest struct {
Slug string `json:"slug" example:"my-space"`
Name string `json:"name" example:"My Space"`
Description *string `json:"description,omitempty" example:"A vector space"`
Type string `json:"type" example:"vector"`
Icon *string `json:"icon,omitempty" example:"rocket"`
IsPrivate bool `json:"is_private" example:"false"`
OwnerTeamID *string `json:"owner_team_id,omitempty" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
Visibility string `json:"visibility,omitempty" example:"discoverable" enums:"hidden,discoverable,org"`
}
SwaggerCreateSpaceRequest matches createSpaceRequest in spaces handler.
type SwaggerCreateSprintRequest ¶ added in v0.1.14
type SwaggerCreateSprintRequest struct {
Name string `json:"name" example:"Sprint 1"`
Goal string `json:"goal" example:"Complete core features"`
StartsAt *time.Time `json:"starts_at,omitempty"`
EndsAt *time.Time `json:"ends_at,omitempty"`
}
SwaggerCreateSprintRequest matches createSprintRequest in projects handler.
type SwaggerCreateTeamRequest ¶ added in v0.3.1
type SwaggerCreateTeamRequest struct {
Slug string `json:"slug" example:"platform"`
Name string `json:"name" example:"Platform"`
Description string `json:"description,omitempty" example:"Platform engineering"`
ParentID *string `json:"parent_id,omitempty" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
}
SwaggerCreateTeamRequest matches createTeamRequest in teams handler.
type SwaggerCreateTicketRequest ¶ added in v0.1.14
type SwaggerCreateTicketRequest struct {
Title string `json:"title" example:"Fix login button"`
Description string `json:"description" example:"The login button does not work on mobile"`
Priority string `json:"priority" example:"medium"`
AssigneeID *uuid.UUID `json:"assignee_id,omitempty"`
DueAt *time.Time `json:"due_at,omitempty"`
}
SwaggerCreateTicketRequest matches createTicketRequest in tickets handler.
type SwaggerErrorDetail ¶ added in v0.1.14
type SwaggerErrorDetail struct {
Code string `json:"code" example:"UNAUTHORIZED"`
Message string `json:"message" example:"invalid email or password"`
RequestID string `json:"request_id,omitempty" example:"req_a8d9912a"`
}
SwaggerErrorDetail is the inner error object.
type SwaggerErrorResponse ¶ added in v0.1.14
type SwaggerErrorResponse struct {
Error SwaggerErrorDetail `json:"error"`
}
SwaggerErrorResponse is the standard error format for all API errors.
type SwaggerHealthResponse ¶ added in v0.1.14
type SwaggerHealthResponse struct {
Status string `json:"status" example:"ok"`
}
SwaggerHealthResponse matches the healthResponse struct in health.go.
type SwaggerKanbanColumn ¶ added in v0.1.14
type SwaggerKanbanColumn struct {
Status string `json:"status" example:"open"`
Tickets []SwaggerTicketResponse `json:"tickets"`
}
SwaggerKanbanColumn represents one column in a kanban board.
type SwaggerLoginRequest ¶ added in v0.1.14
type SwaggerLoginRequest struct {
Email string `json:"email" example:"admin@azimuthal.com"`
Password string `json:"password" example:"yourpassword"`
}
SwaggerLoginRequest is the request body for POST /auth/login.
type SwaggerLoginResponse ¶ added in v0.1.14
type SwaggerLoginResponse struct {
AccessToken string `json:"access_token" example:"eyJhbGciOiJSUzI1NiIs..."`
RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJSUzI1NiIs..."`
Token string `json:"token" example:"eyJhbGciOiJSUzI1NiIs..."`
User SwaggerUserResponse `json:"user"`
Org *SwaggerOrgResponse `json:"org,omitempty"`
}
SwaggerLoginResponse matches the loginResponse struct in auth handler.
type SwaggerLogoutResponse ¶ added in v0.1.14
type SwaggerLogoutResponse struct {
Message string `json:"message" example:"logged out"`
}
SwaggerLogoutResponse is the response body for POST /auth/logout.
type SwaggerMessageResponse ¶ added in v0.1.14
type SwaggerMessageResponse struct {
Message string `json:"message" example:"operation completed"`
}
SwaggerMessageResponse is a generic message response used by several endpoints.
type SwaggerMovePageRequest ¶ added in v0.1.14
type SwaggerMovePageRequest struct {
ParentID *uuid.UUID `json:"parent_id"`
Position int32 `json:"position" example:"1"`
}
SwaggerMovePageRequest matches movePageRequest in wiki handler.
type SwaggerMoveToBacklogRequest ¶ added in v0.1.14
type SwaggerMoveToBacklogRequest struct {
ItemID uuid.UUID `json:"item_id" example:"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
}
SwaggerMoveToBacklogRequest matches moveToBacklogRequest in projects handler.
type SwaggerMoveToSprintRequest ¶ added in v0.1.14
type SwaggerMoveToSprintRequest struct {
ItemID uuid.UUID `json:"item_id" example:"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
SprintID uuid.UUID `json:"sprint_id" example:"b2c3d4e5-f6a7-8901-bcde-f12345678901"`
}
SwaggerMoveToSprintRequest matches moveToSprintRequest in projects handler.
type SwaggerOrgResponse ¶ added in v0.1.14
type SwaggerOrgResponse struct {
ID uuid.UUID `json:"id" example:"9c0e1642-64bc-4745-992e-8e0eec643ee1"`
Slug string `json:"slug" example:"my-org"`
Name string `json:"name" example:"My Organization"`
}
SwaggerOrgResponse matches the orgResponse struct in auth handler.
type SwaggerPatchTeamRequest ¶ added in v0.3.1
type SwaggerPatchTeamRequest struct {
Name *string `json:"name,omitempty" example:"Platform Core"`
Description *string `json:"description,omitempty" example:"Renamed"`
ParentID *string `json:"parent_id,omitempty" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
}
SwaggerPatchTeamRequest matches patchTeamRequest in teams handler. parent_id: absent = unchanged, null = move to root, UUID = reparent.
type SwaggerPutMemberRequest ¶ added in v0.3.1
type SwaggerPutMemberRequest struct {
Role string `json:"role" example:"member" enums:"member,lead"`
IsPrimary bool `json:"is_primary" example:"false"`
}
SwaggerPutMemberRequest matches putMemberRequest in teams handler.
type SwaggerRefreshRequest ¶ added in v0.1.14
type SwaggerRefreshRequest struct {
RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJSUzI1NiIs..."`
}
SwaggerRefreshRequest is the request body for POST /auth/refresh.
type SwaggerRefreshResponse ¶ added in v0.1.14
type SwaggerRefreshResponse struct {
AccessToken string `json:"access_token" example:"eyJhbGciOiJSUzI1NiIs..."`
RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJSUzI1NiIs..."`
}
SwaggerRefreshResponse matches the refreshResponse struct in auth handler.
type SwaggerRegisterRequest ¶ added in v0.1.14
type SwaggerRegisterRequest struct {
Email string `json:"email" example:"newuser@azimuthal.com"`
DisplayName string `json:"display_name" example:"New User"`
Password string `json:"password" example:"securepassword123"`
}
SwaggerRegisterRequest is the request body for POST /auth/register.
type SwaggerRequesterIdentity ¶ added in v0.4.0
type SwaggerRequesterIdentity struct {
ID uuid.UUID `json:"id" example:"c3d4e5f6-a7b8-9012-cdef-123456789012"`
DisplayName string `json:"display_name" example:"Dana Okoro"`
Email string `json:"email" example:"dana@example.com"`
}
SwaggerRequesterIdentity is the external requester behind a portal-raised ticket, as the agent surface sees them. Null on a ticket raised inside the product — see SwaggerTicketResponse.Requester.
type SwaggerSprintAssignRequest ¶ added in v0.1.14
SwaggerSprintAssignRequest matches sprintAssignRequest in projects handler.
type SwaggerStatusRequest ¶ added in v0.1.14
type SwaggerStatusRequest struct {
Status string `json:"status" example:"in_progress"`
}
SwaggerStatusRequest matches statusRequest in projects handler.
type SwaggerTicketResponse ¶ added in v0.1.14
type SwaggerTicketResponse struct {
ID uuid.UUID `json:"id" example:"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`
SpaceID uuid.UUID `json:"space_id" example:"b2c3d4e5-f6a7-8901-bcde-f12345678901"`
Number int32 `json:"number" example:"42"`
Title string `json:"title" example:"Fix login button"`
Description string `json:"description" example:"The login button does not work"`
Status string `json:"status" example:"open"`
Priority string `json:"priority" example:"medium" enums:"urgent,high,medium,low"`
// ReporterID is null exactly when RequesterID is set: migration 044's
// tickets_origin_identity makes the two mutually exclusive.
ReporterID *uuid.UUID `json:"reporter_id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
// RequesterID identifies a portal-raised ticket. Non-null is the whole
// provenance predicate; Requester carries the resolved identity.
RequesterID *uuid.UUID `json:"requester_id"`
Requester *SwaggerRequesterIdentity `json:"requester"`
AssigneeID *uuid.UUID `json:"assignee_id,omitempty"`
DueAt *time.Time `json:"due_at,omitempty"`
ResolvedAt *time.Time `json:"resolved_at,omitempty"`
Rank string `json:"rank" example:"0|aaaaaa:"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
SwaggerTicketResponse represents the Ticket domain object returned by handlers.
Corrected in the portal requester-surface PR against internal/core/tickets.Ticket, which it had drifted from: Priority is a string enum on the wire and was declared int; ReporterID became nullable under migration 044 and was declared non-nullable; Number and RequesterID were absent entirely. Per CLAUDE.md §5 the repository wins and the drift is corrected in the PR that found it.
type SwaggerTransitionRequest ¶ added in v0.1.14
type SwaggerTransitionRequest struct {
Status string `json:"status" example:"in_progress"`
}
SwaggerTransitionRequest matches transitionRequest in tickets handler.
type SwaggerUpdateGrantRequest ¶ added in v0.3.1
type SwaggerUpdateGrantRequest struct {
Role string `json:"role" example:"contributor" enums:"viewer,contributor,agent,space_admin"`
}
SwaggerUpdateGrantRequest matches updateGrantRequest in grants handler.
type SwaggerUpdateItemRequest ¶ added in v0.1.14
type SwaggerUpdateItemRequest struct {
Title string `json:"title" example:"Implement search (updated)"`
Description string `json:"description" example:"Updated description"`
// Kind is the org-defined item-type slug. Must name an active type;
// unknown or archived values are rejected.
Kind string `json:"kind" example:"bug"`
Priority string `json:"priority" example:"high"`
AssigneeID *uuid.UUID `json:"assignee_id,omitempty"`
DueAt *time.Time `json:"due_at,omitempty"`
}
SwaggerUpdateItemRequest matches updateItemRequest in projects handler. The real request type uses pointers to tell "absent" from "empty"; this is the documentation shape, so it keeps plain strings.
type SwaggerUpdateOrgRequest ¶ added in v0.1.14
type SwaggerUpdateOrgRequest struct {
Name string `json:"name" example:"Updated Org"`
Description *string `json:"description,omitempty" example:"Updated description"`
}
SwaggerUpdateOrgRequest matches updateOrgRequest in spaces handler.
type SwaggerUpdatePageRequest ¶ added in v0.1.14
type SwaggerUpdatePageRequest struct {
Title string `json:"title" example:"Getting Started (updated)"`
Content string `json:"content" example:"# Updated Content"`
ExpectedVersion int32 `json:"expected_version" example:"1"`
}
SwaggerUpdatePageRequest matches updatePageRequest in wiki handler.
type SwaggerUpdatePortalRequest ¶ added in v0.4.2
type SwaggerUpdatePortalRequest struct {
// Enabled toggles the portal without discarding its key, so re-enabling
// does not invalidate URLs already handed out. Omit to leave unchanged.
Enabled bool `json:"enabled" example:"true"`
// Name is the portal's public display name. Required non-empty when sent;
// omit to leave unchanged. Renaming never changes the portal key.
Name string `json:"name" example:"Acme Support"`
// Intro is the sign-in page's introduction text. Sending null clears it;
// omitting the key leaves it alone.
Intro string `json:"intro" example:"How can we help?"`
}
SwaggerUpdatePortalRequest matches updatePortalRequest in the portal admin handler. The real type uses respond.OptionalField to tell "absent" from "explicit null"; this is the documentation shape, so it keeps plain values.
type SwaggerUpdateSpaceRequest ¶ added in v0.1.14
type SwaggerUpdateSpaceRequest struct {
Name string `json:"name" example:"Updated Name"`
Description *string `json:"description,omitempty" example:"Updated description"`
Icon *string `json:"icon,omitempty" example:"star"`
IsPrivate bool `json:"is_private" example:"false"`
OwnerTeamID *string `json:"owner_team_id,omitempty" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
Visibility string `json:"visibility,omitempty" example:"org" enums:"hidden,discoverable,org"`
}
SwaggerUpdateSpaceRequest matches updateSpaceRequest in spaces handler.
type SwaggerUpdateSprintRequest ¶ added in v0.1.14
type SwaggerUpdateSprintRequest struct {
Name string `json:"name" example:"Sprint 1 (updated)"`
Goal string `json:"goal" example:"Updated goal"`
StartsAt *time.Time `json:"starts_at,omitempty"`
EndsAt *time.Time `json:"ends_at,omitempty"`
}
SwaggerUpdateSprintRequest matches updateSprintRequest in projects handler.
type SwaggerUpdateTicketRequest ¶ added in v0.1.14
type SwaggerUpdateTicketRequest struct {
Title string `json:"title" example:"Fix login button (updated)"`
Description string `json:"description" example:"Updated description"`
Priority string `json:"priority" example:"high"`
// DueAt is RFC3339. Sending null clears the stored due date; omitting the
// key leaves it alone.
DueAt *time.Time `json:"due_at,omitempty"`
}
SwaggerUpdateTicketRequest matches updateTicketRequest in tickets handler. The real request type uses pointers to tell "absent" from "empty"; this is the documentation shape, so it keeps plain strings.
type SwaggerUserResponse ¶ added in v0.1.14
type SwaggerUserResponse struct {
ID uuid.UUID `json:"id" example:"874d6314-6353-45e9-ab2a-5fe930ea4dbc"`
Email string `json:"email" example:"admin@azimuthal.com"`
DisplayName string `json:"display_name" example:"Admin"`
OrgID string `json:"org_id" example:"9c0e1642-64bc-4745-992e-8e0eec643ee1"`
Role string `json:"role" example:"member"`
IsActive bool `json:"is_active" example:"true"`
}
SwaggerUserResponse matches the userResponse struct in auth handler.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package admin provides the org-administration HTTP surface (P2.5): the People directory and user lifecycle, the member picker search, the access matrix with atomic bulk editing, and the audit log viewer.
|
Package admin provides the org-administration HTTP surface (P2.5): the People directory and user lifecycle, the member picker search, the access matrix with atomic bulk editing, and the audit log viewer. |
|
Package attachments provides HTTP handlers for entity attachments (P3, ADR-0008 rule 3).
|
Package attachments provides HTTP handlers for entity attachments (P3, ADR-0008 rule 3). |
|
Package auth provides HTTP handlers for authentication endpoints.
|
Package auth provides HTTP handlers for authentication endpoints. |
|
Package avatar serves the user avatar surface: self and admin upload plus the org-member-readable serve endpoint.
|
Package avatar serves the user avatar surface: self and admin upload plus the org-member-readable serve endpoint. |
|
Package comments provides HTTP handlers for polymorphic entity comment endpoints.
|
Package comments provides HTTP handlers for polymorphic entity comment endpoints. |
|
Package dashboards provides HTTP handlers for dashboards and gadgets (P5, ADR-0009, spec §6) — composable grids whose data always comes from the saved-view layer.
|
Package dashboards provides HTTP handlers for dashboards and gadgets (P5, ADR-0009, spec §6) — composable grids whose data always comes from the saved-view layer. |
|
Package grants provides HTTP handlers for space grants and the effective-access explanation (v0.3 spec §6).
|
Package grants provides HTTP handlers for space grants and the effective-access explanation (v0.3 spec §6). |
|
Package invites provides the invite HTTP surface (P2.5 W2): the org-admin lifecycle (create, list, revoke, resend) and the public token-authenticated acceptance routes.
|
Package invites provides the invite HTTP surface (P2.5 W2): the org-admin lifecycle (create, list, revoke, resend) and the public token-authenticated acceptance routes. |
|
Package notifications provides HTTP handlers for in-app notification endpoints.
|
Package notifications provides HTTP handlers for in-app notification endpoints. |
|
Package portal serves the customer portal: the unauthenticated sign-in surface and the requester-authenticated request surface.
|
Package portal serves the customer portal: the unauthenticated sign-in surface and the requester-authenticated request surface. |
|
Package projects provides HTTP handlers for project tracking endpoints.
|
Package projects provides HTTP handlers for project tracking endpoints. |
|
Package relations provides HTTP handlers for the polymorphic entity relation endpoints.
|
Package relations provides HTTP handlers for the polymorphic entity relation endpoints. |
|
Package respond provides shared JSON response helpers for HTTP handlers.
|
Package respond provides shared JSON response helpers for HTTP handlers. |
|
Package search serves the cross-module search endpoint (P6, spec §5 and §7).
|
Package search serves the cross-module search endpoint (P6, spec §5 and §7). |
|
Package shares provides HTTP handlers for entity shares (v0.3 spec §6, ADR-0008).
|
Package shares provides HTTP handlers for entity shares (v0.3 spec §6, ADR-0008). |
|
Package spaces provides HTTP handlers for space management and the org space directory (v0.3 spec §6).
|
Package spaces provides HTTP handlers for space management and the org space directory (v0.3 spec §6). |
|
Package swaggerui embeds the Swagger UI static assets that /api/docs needs.
|
Package swaggerui embeds the Swagger UI static assets that /api/docs needs. |
|
Package teams provides HTTP handlers for team management (v0.3 spec §6).
|
Package teams provides HTTP handlers for team management (v0.3 spec §6). |
|
Package ticketref carries the operator-supplied ticket reference that administrative mutations record on their audit events.
|
Package ticketref carries the operator-supplied ticket reference that administrative mutations record on their audit events. |
|
Package tickets provides HTTP handlers for service desk endpoints.
|
Package tickets provides HTTP handlers for service desk endpoints. |
|
Package tiergate adapts the workflow tier chokepoint to the HTTP layer.
|
Package tiergate adapts the workflow tier chokepoint to the HTTP layer. |
|
Package views provides HTTP handlers for saved views (ADR-0009, ADR-0010, spec §6) — named, reusable queries over Beacon tickets and Vector project items.
|
Package views provides HTTP handlers for saved views (ADR-0009, ADR-0010, spec §6) — named, reusable queries over Beacon tickets and Vector project items. |
|
Package wiki provides HTTP handlers for wiki/docs endpoints.
|
Package wiki provides HTTP handlers for wiki/docs endpoints. |
|
Package workflows provides HTTP handlers for workflow engine endpoints.
|
Package workflows provides HTTP handlers for workflow engine endpoints. |