AppSurface Search
API Reference

Controllers

Type

SearchIndexRefreshAuthorizationResult

Source

Represents the internal authorization outcome for an AppSurface Docs search-index refresh request.

Parameters

  • IsAllowedIndicates whether the refresh action may invalidate the search-index cache.
  • ReasonThe denial reason when IsAllowed is false; otherwise null.

Remarks

Use Allowed and Denied(SearchIndexRefreshAuthorizationFailure) to preserve the two valid states: allowed results have IsAllowed == true and no reason, while denied results have IsAllowed == false and a concrete reason. Consumers should check IsAllowed before reading Reason because the reason is intentionally nullable for successful authorization. This immutable record struct is internal to the controller layer and should not become part of a public wire contract.

Method

Allowed

SearchIndexRefreshAuthorizationResult Allowed() Source

Creates an authorization result that permits cache invalidation.

Returns

An allowed result with no failure reason.

Method

Denied

SearchIndexRefreshAuthorizationResult Denied(SearchIndexRefreshAuthorizationFailure reason) Source

Creates an authorization result that denies cache invalidation for a specific reason.

Parameters

  • reasonThe concrete denial reason to expose to internal logging and branch-specific tests.

Returns

A denied result whose Reason is set to reason.

Enum

SearchIndexRefreshAuthorizationFailure

Source

Enumerates the internal reasons a search-index refresh request can be denied before cache invalidation.

Remarks

These reasons intentionally distinguish host setup failures from caller authorization failures so logs and tests can identify the remediation path. They are not serialized to clients; the packaged endpoint returns HTTP 403 for every denial after anti-forgery validation succeeds.

Type

DocsController

Source

Controller for serving documentation pages.

Method

Index

Task<IActionResult> Index() Source

Displays the documentation index view containing either curated proof paths from the repository-root landing doc metadata or the neutral docs landing fallback.

Returns

A view result whose model is a DocLandingViewModel. The model includes curated featured cards when the repository-root README.md metadata authors featured_page_groups through inline front matter or a paired sidecar such as README.md.yml; otherwise it includes the neutral fallback landing data.

Method

VersionEntry

IActionResult VersionEntry() Source

Displays the stable docs entry fallback when versioning is enabled but the recommended released tree is not mounted.

Returns

A view result describing the available released versions plus the current preview surface, or a redirect to the live docs home when versioning is disabled.

Method

Versions

IActionResult Versions() Source

Displays the public docs version archive.

Returns

A view result that lists the published versions described by the configured version catalog, or a redirect to the live docs home when versioning is disabled.

Method

Section

Task<IActionResult> Section(string sectionSlug) Source

Enters one normalized public documentation section.

Parameters

  • sectionSlugThe stable slug for the public section.

Returns

A redirect to the authored landing doc when one exists, otherwise a grouped section fallback or unavailable view.

Method

Details

Task<IActionResult> Details(string path) Source

Displays the full or partial details view for a documentation item identified by the given path.

Parameters

  • pathThe public docs route, redirect alias, or .partial.html resource identifier of the documentation item to retrieve. Full-page source-shaped Markdown routes for public pages redirect to the clean canonical route.

Returns

An IActionResult rendering the details view or the doc-content RazorWire frame; returns NotFoundResult when the path is invalid or no document is found after fallback resolution.

Remarks

Partial requests ending in .partial.html are resolved through the same DocAggregator.GetDocDetailsAsync(string, CancellationToken) flow as full-page requests. When a partial path resolves to an /index resource, such as /index.partial.html, the action transparently retries the parent document before returning NotFoundResult. Successful requests load the complete docs corpus and public-section snapshots with DocAggregator.GetDocsAsync(CancellationToken) and DocAggregator.GetPublicSectionsAsync(CancellationToken), then build the response model with BuildDetailsViewModel. All aggregator calls observe HttpContext.RequestAborted. Visible caller side effects are limited to returning either the full details view or a doc-content frame for partial navigation.

Method

Search

Method

SearchIndex

Task<IActionResult> SearchIndex() Source

Returns docs search index data for live-hosted docs.

Returns

A JSON result containing searchable document metadata, including normalized page-type badge fields that keep search result rendering consistent with the built-in landing and details experiences. When HttpRequest.PathBase is non-empty, only documents[*].path values that already start with / are rebased onto that path base before serialization so a mounted app returns links like /some-base/docs/guide.html instead of /docs/guide.html.

Remarks

The path-base rewrite is intentionally narrow. Only rooted documents[*].path values are prefixed; blank, missing, or already non-rooted values such as guide.html remain unchanged. The rewrite trims trailing slashes from the request path base before concatenation, so /some-base/ and /some-base produce the same output. For example, a typed payload that contains documents[0].path = "/docs/guide.html" becomes /some-base/docs/guide.html when the request path base is /some-base. This action always receives the typed DocsSearchIndexPayload produced by DocAggregator.GetSearchIndexPayloadAsync(System.Threading.CancellationToken); raw JSON payloads without a top-level documents array are outside this method's contract and must be handled before this action is invoked. The rewrite is idempotent when HttpRequest.PathBase is null, empty, or /.

Method

RefreshSearchIndex

Task<IActionResult> RefreshSearchIndex() Source

Invalidates the current live docs search-index cache after host-owned operator authorization succeeds.

Returns

NoContentResult when the configured policy authorizes the current user; otherwise HTTP 403. MVC anti-forgery validation runs before this action body and rejects missing or invalid tokens before policy checks.

Remarks

This endpoint is intentionally POST-only and side-effecting. Readers should fetch SearchIndex; host operators should post to DocsUrlBuilder.Routes.SearchIndexRefresh with a valid anti-forgery token and a user that satisfies the effective AppSurface Docs operator-write policy. New hosts should configure AppSurfaceDocsDiagnosticsOptions.OperatorWritePolicy; existing hosts may keep AppSurfaceDocsDiagnosticsOptions.SearchIndexRefreshPolicy as the compatibility fallback.

Method

Harvest

Task<IActionResult> Harvest(string? returnUrl = null, string? rebuild = null) Source

Displays the live harvest observatory for a trusted operator rebuild or an active startup harvest.

Parameters

  • returnUrlOptional app-relative docs URL to revisit after the active harvest completes. Unsafe values and harvest-loop targets fall back to the current docs home.
  • rebuildOptional rebuild request result emitted by RebuildHarvest so the observatory can show whether the operator request started, queued, or was already queued.

Returns

The harvest observatory while a harvest is active or queued; otherwise a redirect to the validated return URL.

Method

RebuildHarvest

Task<IActionResult> RebuildHarvest(string? returnUrl = null) Source

Starts or queues a full source-backed AppSurface Docs harvest rebuild after trusted operator authorization.

Parameters

  • returnUrlOptional app-relative docs URL to revisit after rebuild completion. Unsafe values, non-docs paths, and harvest loop targets fall back to the current docs home.

Returns

A redirect to the live harvest observatory when authorization succeeds; otherwise HTTP 403. MVC anti-forgery validation runs before this action body.

Method

HarvestHealth

Task<IActionResult> HarvestHealth() Source

Displays the redacted operator-facing harvest health page for the current live docs surface.

Returns

A health page when health routes are exposed for the current environment; otherwise NotFoundResult. Healthy and empty snapshots return HTTP 200. Degraded and failed snapshots render the same page with HTTP 503 so local verification and CI checks can fail quickly.

Method

HarvestHealthJson

Task<IActionResult> HarvestHealthJson() Source

Returns redacted machine-readable harvest health for the current live docs surface.

Returns

A JSON health response when health routes are exposed for the current environment; otherwise NotFoundResult. Healthy and empty snapshots return HTTP 200 with verification.ok=true. Degraded and failed snapshots return HTTP 503 with verification.ok=false.

Method

RouteInspector

Task<IActionResult> RouteInspector(string? path = null) Source

Displays the human-facing route inspector for the current live docs surface.

Parameters

  • pathOptional path to probe. Values are trimmed, may be docs-root-relative or app-relative, may include the active HttpRequest.PathBase, and have any query string or fragment stripped before route lookup. Absolute URLs, protocol-relative URLs, paths outside the active docs root, empty post-strip values, and . or .. path segments produce an invalid-input probe instead of route lookup.

Returns

A no-store route inspector page when diagnostics are exposed for the current environment; otherwise NotFoundResult. The page uses BuildRouteInspectorResponseAsync(string?) for the same manifest and optional probe shape as the JSON endpoint.

Remarks

Use this endpoint for interactive maintainer inspection. It is intentionally separate from reader navigation and is hidden by AppSurfaceDocsDiagnosticsVisibility.IsRouteInspectorExposed(AppSurfaceDocsOptions, IHostEnvironment) when the current environment or explicit diagnostics settings do not expose route diagnostics.

Method

RouteInspectorJson

Task<IActionResult> RouteInspectorJson(string? path = null) Source

Returns machine-readable route identity for the current live docs surface.

Parameters

  • pathOptional path to probe. Values are trimmed, may be docs-root-relative or app-relative, may include the active HttpRequest.PathBase, and have any query string or fragment stripped before route lookup. Absolute URLs, protocol-relative URLs, paths outside the active docs root, empty post-strip values, and . or .. path segments produce an invalid-input probe in the JSON response.

Returns

A no-store JSON route inspector response when diagnostics are exposed for the current environment; otherwise NotFoundResult.

Remarks

Use this endpoint for scripts, tests, and maintainer tools that need the AppSurfaceDocsRouteInspectorResponse wire contract produced by BuildRouteInspectorResponseAsync(string?). Use RouteInspector(string?) instead when a human needs the compact HTML probing surface.

Method

CollectMetrics

Task<IActionResult> CollectMetrics() Source

Accepts low-trust browser AppSurface Docs metrics submissions for hosted collection.

Returns

NoContentResult for accepted, invalid, or dropped submissions while hosted collection is enabled; otherwise NotFoundResult. Unsupported media type and oversized bodies return HTTP 415 and 413 respectively without echoing submitted values.

Remarks

Route mapping constrains this collector to HTTP POST. The action intentionally opts out of antiforgery validation because it is an anonymous, low-trust JSON collector for hosted docs and static exports rather than an authenticated state-changing form post. The request body uses a narrow DTO containing only name, properties, and an optional client timestamp. Browser-supplied identity, route, URL, cookies, headers, and request metadata are not accepted into the event envelope. Every submitted event is revalidated through AppSurfaceProductEventRegistry before the process-local read model or host-owned product-intelligence sinks see it.

Method

SearchQuality

IActionResult SearchQuality() Source

Displays recent hosted AppSurface Docs search-quality diagnostics.

Returns

A no-store diagnostics page when hosted review is enabled and exposed; otherwise NotFoundResult.

Method

AuthorizeSearchIndexRefreshAsync

Task<SearchIndexRefreshAuthorizationResult> AuthorizeSearchIndexRefreshAsync(CancellationToken cancellationToken) Source

Authorizes a side-effecting search-index refresh request against the host-configured operator policy.

Parameters

  • cancellationTokenToken observed while resolving the configured authorization policy; caller cancellation does not turn policy or authorization failures into exceptions.

Returns

A SearchIndexRefreshAuthorizationResult whose allowed state means cache invalidation may proceed. Denied results carry the specific SearchIndexRefreshAuthorizationFailure reason for logging and explicit 403 responses.

Remarks

The decision flow is intentionally ordered from host configuration to caller identity: a missing or blank no effective AppSurfaceDocsDiagnosticsOptions.OperatorWritePolicy or AppSurfaceDocsDiagnosticsOptions.SearchIndexRefreshPolicy fallback returns SearchIndexRefreshAuthorizationFailure.MissingPolicyOption; missing HttpContext, request services, or IAuthorizationPolicyProvider returns SearchIndexRefreshAuthorizationFailure.MissingPolicyProvider; a missing IAuthorizationService returns SearchIndexRefreshAuthorizationFailure.MissingAuthorizationService; an unresolved configured policy returns SearchIndexRefreshAuthorizationFailure.PolicyNotFound; a missing or unauthenticated user returns SearchIndexRefreshAuthorizationFailure.Unauthenticated; and a policy evaluation failure returns SearchIndexRefreshAuthorizationFailure.AuthorizationFailed. Authorization denials are reported as denied results rather than thrown exceptions. The policy lookup uses Task.WaitAsync(CancellationToken) so cancellation can interrupt a slow policy provider before refresh side effects occur.

Method

BuildHarvestUrlWithReturnUrl

string BuildHarvestUrlWithReturnUrl(string returnUrl, AppSurfaceDocsHarvestRebuildRequestResult rebuild) Source

Builds a path-base-aware harvest observatory URL that carries the verified docs return URL and rebuild result.

Parameters

  • returnUrlThe app-relative docs URL to revisit after the active or queued harvest completes.
  • rebuildThe trusted rebuild request result to expose in the observatory status copy.

Returns

A local harvest URL containing URL-encoded returnUrl and rebuild query-string values.

Remarks

Callers should pass only return URLs produced by ResolveHarvestReturnUrl(string?) or the current docs request. This helper only encodes the value for transport; it does not re-run the docs-root containment policy. Unknown rebuild enum values serialize as an empty marker and are ignored by the observatory parser.

Method

ResolveHarvestReturnUrl

string ResolveHarvestReturnUrl(string? returnUrl) Source

Resolves request-provided harvest navigation back to a safe docs URL.

Parameters

  • returnUrlThe candidate app-relative return URL from the query string or rebuild form.

Returns

returnUrl when it stays under the current docs root and avoids harvest routes; otherwise the path-base-aware docs home URL.

Remarks

This is the docs-specific return URL policy for the harvest loop. It intentionally rejects same-origin paths outside the active HttpRequest.PathBase and docs surface, raw or encoded traversal, and _harvest loops so a terminal progress update cannot navigate an operator away from the docs context being verified.

Method

ResolveDocsOperatorWritePolicyName

string? ResolveDocsOperatorWritePolicyName() Source

Resolves the authorization policy used for trusted docs write operations.

Returns

The configured docs operator-write policy when present, otherwise the legacy search-index refresh policy, or null when neither policy is configured.

Remarks

AppSurfaceDocsDiagnosticsOptions.OperatorWritePolicy is preferred so hosts can give rebuild actions a neutral docs-operator policy name. The fallback to AppSurfaceDocsDiagnosticsOptions.SearchIndexRefreshPolicy preserves compatibility for applications that already opted into the older refresh endpoint.

Method

GetRebuildAuthorizationStatus

string GetRebuildAuthorizationStatus(SearchIndexRefreshAuthorizationFailure? failure) Source

Converts an authorization failure reason into the compact health-page rebuild status.

Parameters

  • failureThe failure reason returned by the shared operator authorization helper.

Returns

Unauthorized for user/auth policy denials; otherwise Unavailable.

Remarks

Missing policies and missing services are shown as unavailable rather than unauthorized so operators can distinguish host configuration problems from account permission problems.

Method

GetRebuildAuthorizationDescription

string GetRebuildAuthorizationDescription(SearchIndexRefreshAuthorizationFailure? failure) Source

Builds the visible health-page explanation for a disabled rebuild action.

Parameters

  • failureThe failure reason returned by the shared operator authorization helper.

Returns

A short operator-facing explanation for why the rebuild form is disabled.

Remarks

The text avoids exposing policy internals while still separating sign-in, authorization, missing-policy, and unavailable-service cases for production troubleshooting.

Method

GetHarvestRebuildRequestResultQueryValue

string GetHarvestRebuildRequestResultQueryValue(AppSurfaceDocsHarvestRebuildRequestResult result) Source

Gets the stable query-string marker used to carry a harvest rebuild request result into the observatory view.

Parameters

  • resultThe rebuild request result returned by the shared harvest coordinator.

Returns

The stable marker for a known result, or an empty string for an unknown enum value.

Method

ParseHarvestRebuildRequestResult

AppSurfaceDocsHarvestRebuildRequestResult? ParseHarvestRebuildRequestResult(string? value) Source

Parses the stable query-string marker used by the harvest observatory rebuild status.

Parameters

  • valueThe raw rebuild query-string value.

Returns

The matching rebuild request result, or null for missing, blank, or unknown markers.

Remarks

Unknown values are ignored instead of displayed so stale links and hand-written URLs do not create misleading operator status copy.

Method

IsSafeDocsHarvestReturnUrl

bool IsSafeDocsHarvestReturnUrl(string? url, string? pathBase, string docsRootPath) Source

Determines whether a harvest completion return URL stays inside the active docs surface and cannot loop back to the harvest observatory.

Parameters

  • urlThe candidate app-relative return URL.
  • pathBaseThe active request path base, or null when none is mounted.
  • docsRootPathThe configured current docs root path.

Returns

true when url is a safe app-relative path under the current docs root and not _harvest or _harvest/rebuild; otherwise false.

Method

TryNormalizeReturnUrlPathForValidation

bool TryNormalizeReturnUrlPathForValidation(string path, out string normalizedPath) Source

Normalizes a harvest return URL path for containment checks after rejecting encoded traversal tricks.

Parameters

  • pathThe path portion of the candidate return URL.
  • normalizedPathThe normalized decoded path when validation succeeds.

Returns

true when path can be decoded without revealing control characters, backslashes, raw dot segments, or encoded dot-segment traversal; otherwise false.

Method

TryValidateReturnUrlPercentEscapes

bool TryValidateReturnUrlPercentEscapes(string path) Source

Rejects malformed percent escapes and double-encoded sensitive path tokens in a harvest return URL path.

Parameters

  • pathThe path portion of the candidate return URL.

Returns

true when every percent escape is syntactically valid and does not hide a second encoded control character, slash, backslash, or dot; otherwise false.

Method

IsSafeAppRelativeUrl

bool IsSafeAppRelativeUrl(string? url) Source

Determines whether a return URL is safe to use as an app-relative navigation target.

Parameters

  • urlThe candidate URL to validate.

Returns

true when url is a non-empty app-relative path that starts with /, is not protocol-relative, is not slash-backslash rooted, and contains no backslashes or control characters; otherwise false.

Remarks

Use IsSafeAppRelativeUrl(string?) before echoing request-derived return URLs into redirects, links, or local path decisions. The helper intentionally rejects null, empty or whitespace input, paths that do not start with /, //, /\, any \ character, and any control character such as CR or LF. Checks are ordinal and culture-invariant; the method does not URL-decode or normalize Unicode, so callers should decode first when validating encoded input.

Method

NormalizeReturnUrlPath

string NormalizeReturnUrlPath(string? path) Source

Normalizes a local return URL path into the shape used by docs containment checks.

Parameters

  • pathA candidate path, path base, or docs root value.

Returns

A leading-slash path with trailing slashes removed, or an empty string for blank input.

Remarks

The helper only trims and applies slash shape. It does not decode, collapse dot segments, or decide whether a path is safe; callers must validate encoded input and traversal before using the normalized value for redirects.

Method

ContainsDotSegment

bool ContainsDotSegment(string path) Source

Detects dot-segment traversal after splitting a return URL path on literal path separators.

Parameters

  • pathThe raw or once-decoded path being evaluated for harvest return navigation.

Returns

true when any path segment is exactly . or ..; otherwise false.

Remarks

This helper is deliberately segment-based instead of substring-based so ordinary filenames containing dots remain valid. Callers must invoke it for both the raw path and the once-decoded path because percent-encoded traversal can be hidden until after decoding.

Method

ContainsSensitivePercentEscape

bool ContainsSensitivePercentEscape(string path) Source

Detects percent escapes that still hide sensitive path bytes after the first decode pass.

Parameters

  • pathThe once-decoded path being checked for nested percent escapes.

Returns

true when path contains an encoded control character, slash, backslash, or dot; otherwise false.

Remarks

Harvest return URLs are decoded once before redirect decisions. A second decoder later in the stack could otherwise turn split encodings such as %25%32%65 into %2e and then into .. Rejecting sensitive nested escapes keeps the docs-only containment decision stable across downstream URL normalization.

Method

StartsWithHarvestRouteSegment

bool StartsWithHarvestRouteSegment(string relativePath) Source

Determines whether a docs-root-relative path begins with the reserved harvest route segment.

Parameters

  • relativePathThe decoded path relative to the current docs root.

Returns

true when the first non-empty path segment is _harvest; otherwise false.

Remarks

Duplicate separators can appear after decoding an otherwise local URL. The harvest loop guard therefore evaluates the first meaningful segment instead of comparing the unnormalized relative path string.

Method

IsHex

bool IsHex(char value) Source

Determines whether a character can participate in a percent-encoded byte.

Parameters

  • valueThe candidate hexadecimal digit.

Returns

true for ASCII hexadecimal digits; otherwise false.

Remarks

URL percent escapes are byte-oriented and ASCII-only. This intentionally does not accept Unicode lookalikes or culture-sensitive digits, and callers should only pass characters adjacent to a literal %.

Method

HexToByte

int HexToByte(char high, char low) Source

Converts two validated hexadecimal digits into the byte value represented by a percent escape.

Parameters

  • highThe high-order hexadecimal digit.
  • lowThe low-order hexadecimal digit.

Returns

The decoded byte value from 0 through 255.

Remarks

Callers must guard both inputs with IsHex(char) before calling this helper. The method is intentionally allocation-free because it runs in the return-URL validation hot path.

Method

HexValue

int HexValue(char value) Source

Maps a single validated hexadecimal digit to its numeric value.

Parameters

  • valueThe ASCII hexadecimal digit to convert.

Returns

An integer from 0 through 15.

Remarks

This helper assumes value has already passed IsHex(char). Passing any other character falls into the uppercase branch and produces a meaningless value, so validation order is part of the contract.

Method

IsUnderPath

bool IsUnderPath(string candidatePath, string rootPath) Source

Determines whether a normalized candidate path is at or below a normalized root path.

Parameters

  • candidatePathThe normalized candidate path to evaluate.
  • rootPathThe normalized root path that bounds the allowed surface.

Returns

true when candidatePath equals rootPath or is a child path; otherwise false.

Remarks

The root path / accepts any absolute app path. Non-root comparisons are ordinal-ignore-case to match the existing docs route policy. Callers must pass already-normalized, decoded, traversal-free paths.

Method

PrefixSearchIndexPathsForPathBase

DocsSearchIndexPayload PrefixSearchIndexPathsForPathBase(DocsSearchIndexPayload payload, string? requestPathBase) Source

Prefixes rooted search-document paths in a cached search-index payload for the active request path base.

Parameters

  • payloadThe cached search-index payload whose document paths may need rebasing.
  • requestPathBaseThe current request path base that should be prepended when it is non-empty and not /.

Returns

The original payload when no rewrite is needed; otherwise a cloned payload whose rooted DocsSearchIndexDocument.Path values are prefixed with the normalized path base.

Remarks

This helper operates on the typed DocsSearchIndexPayload contract, which always exposes a top-level DocsSearchIndexPayload.Documents list. It does not inspect or reshape arbitrary JSON payloads, so callers that hold raw JSON without a top-level documents array must deserialize or otherwise handle that mismatch before calling this method. Within the typed payload, only rooted DocsSearchIndexDocument.Path values are rewritten. Non-rooted, blank, or otherwise unchanged values such as guide.html are returned as-is, so callers should supply leading-slash browser paths for docs-local navigation when rebasing is expected. For example, rebasing a payload that contains documents[0].path = "/docs/guide.html" with /some-base/ produces /some-base/docs/guide.html. The supplied path base is trimmed of trailing slashes before concatenation, and the method is idempotent when requestPathBase is null, empty, or /.

Method

IsApiSurfaceDoc

bool IsApiSurfaceDoc(DocNode doc) Source

Determines whether a documentation node should render with the API reference reading surface.

Remarks

Non-Markdown generated docs use the API surface by default because AppSurface Docs cannot assume extensionless generated pages have authored prose rhythm. Markdown docs opt into the API surface only when page_type normalizes to api or api-reference. Extensionless authored content is therefore treated as generated API/reference content unless a future harvester exposes a stronger authorship signal.

Method

IsApiSurfacePageType

bool IsApiSurfacePageType(string? pageType) Source

Determines whether raw page-type metadata explicitly requests the API reference reading surface.

Remarks

Values are normalized with DocMetadataPresentation.NormalizeToken(string?) before comparison, so values such as api_reference and API Reference match api-reference. Null or blank metadata does not opt a Markdown document into the API surface.

Method

IsMarkdownDoc

bool IsMarkdownDoc(string path) Source

Determines whether a source path represents authored Markdown by checking known Markdown filename suffixes.

Remarks

Matching is case-insensitive and currently recognizes .md and .markdown. Callers should pass a non-null harvested path; extensionless paths are intentionally treated as non-Markdown generated docs.