Internal Maintenance API
Purpose and Scope
The internal maintenance API is the operational surface OpenWiki uses to keep published repository wikis usable after the initial generation step. These routes are not public product APIs for browser clients; they are server-to-server entry points that coordinate repository refresh scans and cache invalidation. A refresh scan decides which repositories should be queued for regeneration, while revalidation tells the Next.js application which public pages should be regenerated or invalidated after repository state changes. Together, they preserve the app’s promise of living, source-grounded documentation without requiring a user to manually revisit every repository route.
Sources: app/api/internal/refresh-repositories/route.ts, app/api/internal/revalidate-repository/route.ts
This page covers two route handlers under app/api/internal: POST /api/internal/refresh-repositories and POST /api/internal/revalidate-repository. It also covers their shared authorization boundary in agent/lib/route-auth.ts and the eve schedule that runs the same refresh operation daily in agent/schedules/refresh-repositories.ts. The important distinction is that the route handlers are HTTP-maintenance endpoints, while the schedule is an agent-side timer that invokes the underlying refresh library directly and then asks the web app to revalidate the home page.
Sources: app/api/internal/refresh-repositories/route.ts, agent/lib/route-auth.ts, agent/schedules/refresh-repositories.ts
Relevant Source Files
app/api/internal/refresh-repositories/route.ts- Defines the internal POST endpoint that authenticates a request, optionally validates refresh tuning parameters, callsrefreshRepositories, and attempts home-page revalidation afterward.app/api/internal/revalidate-repository/route.ts- Defines the internal POST endpoint that authenticates a request, validates a home or repository revalidation payload, and callsrevalidatePathfor affected public wiki routes.agent/lib/route-auth.ts- Centralizes internal route authentication by composing everouteAuth, local development authorization, and Vercel OIDC subject restrictions.agent/schedules/refresh-repositories.ts- Registers the daily eve schedule for repository refresh work and logs the result of the scheduled scan and enqueue operation.
Authorization Boundary
Both internal route handlers begin by calling authenticateOpenWikiRequest(request). That helper delegates to eve’s routeAuth with the openWikiRouteAuth configuration, so authentication is intentionally centralized rather than reimplemented in each endpoint. In development, the configuration includes localDev(), which makes local operational testing possible. In all environments, it includes vercelOidc(resolveVercelOidcOptions()), giving deployed environments an OIDC-based trust boundary instead of relying on an ordinary shared secret in each handler.
Sources: agent/lib/route-auth.ts, app/api/internal/refresh-repositories/route.ts, app/api/internal/revalidate-repository/route.ts
The OIDC options are optionally narrowed by OPENWIKI_WEB_PROJECT_NAME and OPENWIKI_WEB_TEAM_SLUG. When both are configured, resolveVercelOidcOptions() returns a subjects array containing a Vercel subject with environment: "*", the configured project name, and the configured team slug. That means the route-auth module can restrict accepted Vercel-issued identities to the expected project and team. If either value is absent, the function returns undefined, leaving the OIDC helper to use its default behavior.
Sources: agent/lib/route-auth.ts
The handlers treat authentication failures as complete responses. Each route stores the result of authenticateOpenWikiRequest, checks whether it is a Response, and immediately returns it when authorization did not pass. All parsing and state-changing work happens after that check. This is a useful implementation pattern for maintenance APIs because validation errors can then describe only request-shape problems, while authentication problems remain owned by the shared route-auth layer.
Sources: app/api/internal/refresh-repositories/route.ts, app/api/internal/revalidate-repository/route.ts
Refresh Repositories Endpoint
POST /api/internal/refresh-repositories runs a bounded repository refresh operation. The handler exports maxDuration = 800, matching OpenWiki’s long-running generation and chat routes: refresh scans may need enough time to inspect repository state, enqueue jobs, and handle featured repositories. The request body is optional. If the request has an application/json content type, the route attempts to parse JSON; malformed JSON returns 400 with Expected a JSON request body. If the content type is not JSON, the route proceeds with an undefined payload and uses default refresh behavior.
Sources: app/api/internal/refresh-repositories/route.ts
The accepted JSON body is validated with zod and may include four tuning fields: scanLimit, enqueueLimit, generatorEnqueueLimit, and retryCooldownHours. scanLimit must be a positive integer no greater than 500. enqueueLimit must be a nonnegative integer no greater than 25. generatorEnqueueLimit must be a nonnegative integer no greater than 50. retryCooldownHours must be a nonnegative integer no greater than 336, which is fourteen days. Invalid payloads return a 400 response with the first zod issue message when available.
Sources: app/api/internal/refresh-repositories/route.ts
After validation, the handler calls refreshRepositories with the parsed tuning values and the original request. Passing the request lets the refresh layer retain request-context information where needed, while the limits let operators reduce or expand a particular maintenance run without changing code. The response merges { ok: true } with the result returned by refreshRepositories, so clients should expect the domain-specific refresh counters and limits to be provided by that shared library rather than by the route itself.
Sources: app/api/internal/refresh-repositories/route.ts
The route then attempts to revalidate the home page through requestHomeRevalidation. This step is best-effort: failures are caught and logged with the message Internal repository refresh could not revalidate the home page. and do not cause the refresh response to fail. In development, the handler passes webUrl using the request origin, which helps local web-to-agent or agent-to-web calls resolve against the current dev server. In non-development deployments, it allows the helper to resolve its target through the normal environment.
Sources: app/api/internal/refresh-repositories/route.ts
Refresh request reference
| Field | Type and limits | Behavior |
|---|---|---|
scanLimit | Positive integer, maximum 500 | Caps how many repositories the refresh scan considers. |
enqueueLimit | Nonnegative integer, maximum 25 | Caps normal repository refresh jobs enqueued by one request. |
generatorEnqueueLimit | Nonnegative integer, maximum 50 | Caps generator refresh jobs enqueued by one request. |
retryCooldownHours | Nonnegative integer, maximum 336 | Controls how soon previously failed refresh candidates may be retried. |
Example request:
curl -X POST "$OPENWIKI_URL/api/internal/refresh-repositories" \
-H "content-type: application/json" \
-d '{"scanLimit":100,"enqueueLimit":10}'The example shows the payload shape only. In production, the caller must also satisfy the Vercel OIDC route authentication configured by agent/lib/route-auth.ts; this is not a public unauthenticated curl endpoint.
Sources: app/api/internal/refresh-repositories/route.ts, agent/lib/route-auth.ts
Revalidate Repository Endpoint
POST /api/internal/revalidate-repository invalidates cached public routes after home-page or repository-specific changes. Unlike the refresh endpoint, this route always expects a JSON body. A body parse failure returns 400 with Expected a JSON request body. The payload is validated against a zod union with two shapes: a home revalidation shape and a repository revalidation shape. This union keeps the endpoint small while allowing a caller to use the same authenticated maintenance route for global home updates and repository wiki updates.
Sources: app/api/internal/revalidate-repository/route.ts
For home-only revalidation, the request body is { "kind": "home" }. The handler calls revalidatePath("/") and responds with { ok: true, revalidatedPaths: ["/"] }. This is the only branch that returns the explicit revalidatedPaths array. It is useful when featured repository metadata, repository lists, or other home-page data has changed without a specific wiki revision to report in the response.
Sources: app/api/internal/revalidate-repository/route.ts
For repository revalidation, the request body includes owner, repo, repositoryId, and revisionId, with kind optional and defaulting structurally to the repository branch when it is absent. The handler builds the repository href with getRepoHref({ owner, name: repo }), revalidates /, and revalidates the repository landing route. It then loads the stored wiki through getRepositoryWiki and revalidates every wiki page route except the overview slug. The overview page is excluded because the repository landing route is already the canonical route for that content.
Sources: app/api/internal/revalidate-repository/route.ts
The repository response returns { ok: true, repositoryId, revisionId }. It does not return the list of page paths it revalidated, so callers should treat the request as a command rather than a detailed route inventory. The route’s behavior depends on the currently stored wiki pages: if no wiki is returned, the optional chaining and empty-array fallback make the page loop a no-op after the home and repository landing paths are revalidated. That makes the endpoint safe to call around revision publication boundaries where wiki artifacts may be changing.
Sources: app/api/internal/revalidate-repository/route.ts
Revalidation request reference
| Body shape | Required fields | Paths affected | Response highlights |
|---|---|---|---|
| Home | kind: "home" | / | Returns revalidatedPaths: ["/"]. |
| Repository | owner, repo, repositoryId, revisionId; optional kind: "repository" | /, repository href, and stored wiki page routes except overview | Returns the supplied repositoryId and revisionId. |
Example repository request:
{
"kind": "repository",
"owner": "vercel-labs",
"repo": "openwiki",
"repositoryId": "repo_123",
"revisionId": "rev_456"
}Scheduled Refresh Flow
The scheduled path in agent/schedules/refresh-repositories.ts is the automated counterpart to the refresh endpoint. It registers an eve schedule with cron expression 0 8 * * *, so the repository refresh operation is set up as a daily task. The schedule’s run function receives waitUntil and uses it to run runRepositoryRefresh() in the background. That mirrors serverless maintenance patterns where the scheduler acknowledges the trigger while the platform keeps the asynchronous work alive.
Sources: agent/schedules/refresh-repositories.ts
Inside runRepositoryRefresh, the schedule calls refreshRepositories() without request-specific tuning values. It then attempts home-page revalidation with requestHomeRevalidation(), logs a non-fatal error if revalidation fails, and finally writes a completion log containing the configured limits and observed counters. The logged fields include enqueueLimit, generatorEnqueueLimit, queued, queuedGeneratorRefreshes, scanned, and featuredErrors. Those names are useful operational signals when diagnosing whether the daily task is scanning enough repositories or encountering featured-repository failures.
Sources: agent/schedules/refresh-repositories.ts
Implementation Details and Operational Guidance
The maintenance design separates refresh from revalidation on purpose. Refreshing decides what work should be enqueued or retried; revalidation clears stale public routes after state changes are available. The refresh endpoint calls home revalidation after it completes because a refresh scan can affect repository freshness indicators or featured data. The dedicated revalidation endpoint is more precise: it can invalidate a single repository’s landing route and every generated wiki page route that exists in stored wiki metadata.
Sources: app/api/internal/refresh-repositories/route.ts, app/api/internal/revalidate-repository/route.ts
Operators should prefer the schedule for routine upkeep and reserve the HTTP endpoints for platform integrations, manual maintenance, or callbacks from generation workflows. When testing locally, remember that development authorization and development webUrl behavior are intentionally different from production. When deploying, configure the web project and team values if you want the Vercel OIDC subject check to be scoped tightly. For broader context, read the pages on keeping wikis fresh, indexing jobs, rate limits and auth, and the agent architecture next.