Exact preview identity and explicit runtime reads¶
CLI v0.10.0 includes app previews show, open and logs with --id.
These commands require a compatible platform exposing the preview/environment
identity fields below. Check astro version, astro app previews --help and the
installation's API schema before using the examples. Runtime resource reads
still require current permissions and a readable workload target.
The live-stream and export proof fields described below require the additive
server contract advertising previews.reviewed_live_logs and
previews.reviewed_log_exports. This documentation prepares that contract; it
does not establish that your installation has deployed it. CLI v0.10.0 continues
to use historical polling and has no live WebSocket or log-export command.
Preview catalog browsing and preview detail have separate APIs. Use the immutable
preview GUID for detail; a branch, pull request number, retained hostname, or
conventional environment name does not identify a workload target.
query Preview($id: GUID!, $includeRuntimeResources: Boolean = false) {
astroliftPreviewEnvironment(id: $id, includeRuntimeResources: $includeRuntimeResources) {
id version registeredAppSlug prNumber isManual branch status hostname
tornDownAt ttlUntil isPinned pinnedAt pinnedByEmail pinReason
environmentStatus runtimeStatus
environment {
previewId previewVersion appId appVersion appSlug
environmentId environmentVersion environmentName
clusterId clusterVersion namespace
}
aggregateResources { cpuCores memoryBytes podCount }
}
}
The exact read requires APP_READ, tenant visibility, and token scope. A missing,
soft-deleted, foreign, or inaccessible preview returns null. The API never
substitutes a different preview sharing a PR, branch, or hostname. It reads the
persisted PreviewEnvironment.app_environment foreign key and validates its app,
organization, cluster and namespace ownership. The current database requires this
foreign key; defensive projection also handles unavailable historical bindings.
environmentStatus is available, retired, or unavailable. An owned retired
environment remains identifiable for historical metadata. Its hostname can remain
in the record, but it is not an action target. An unavailable binding exposes no
target. Manual preview identity, lifecycle timestamps, pin and TTL remain metadata.
Basic exact reads, paged browsing, and the deprecated unpaged catalog never list
pods. They report runtimeStatus: not_requested and empty resource aggregates.
Set includeRuntimeResources: true to request one bounded runtime resource read
for the exact available target. A provider failure reports unavailable; it is
not a measurement of zero resources. Retired or unavailable targets do not
trigger provider reads even with that option enabled.
Bound logs and deployment history¶
Retain the exact target's environment GUID, preview version, and environment version when requesting logs or deployment history:
query PreviewDeployments(
$id: GUID!, $environmentId: GUID!, $previewVersion: Int!,
$environmentVersion: Int!, $after: String
) {
astroliftPreviewDeploymentsPage(
id: $id, expectedEnvironmentId: $environmentId,
ifMatchPreviewVersion: $previewVersion,
ifMatchEnvironmentVersion: $environmentVersion, limit: 20, after: $after
) {
items { id version appId environmentId status triggerKind commitSha imageTag createdAt startedAt endedAt }
nextCursor totalCount
}
}
astroliftAppLogs accepts four additive nullable arguments: previewId,
expectedEnvironmentId, ifMatchPreviewVersion, and ifMatchEnvironmentVersion.
A preview request supplies all four together and retains them on every log page
and follow request. It requires APP_READ_LOGS. If environmentName is also sent,
it must equal the canonical target name; it does not select a replacement target.
Partial proof, changed bindings or versions, retirement, and namespace mismatch
produce PRECONDITION before the provider is called. These reads never infer a
namespace or cluster from a name or URL. Ordinary app logs without preview proof
retain their existing behavior.
Deployment history requires APP_READ, filters the exact app and environment
foreign keys, and reads stored deployment snapshots without
computed deployment-status provider calls. Its cursor is scoped to that preview
and environment. A stale or retired target is refused rather than substituted.
Exact live streams and log exports¶
The additive live/export contract extends the four historical-log selectors to
astroliftOnAppLog, astroliftOnAppLogs, and ExportAppLogsInput. Require a
server advertising previews.reviewed_live_logs and previews.reviewed_log_exports
before using these fields. Capability metadata is informational; live logs still
require APP_READ_LOGS, and exports require APP_LOG_EXPORT, at the actual app
and preview environment operation context. Neither capability grants access.
subscription PreviewTail(
$app: String!, $preview: GUID!, $environment: GUID!,
$previewVersion: Int!, $environmentVersion: Int!
) {
astroliftOnAppLogs(
appSlug: $app, previewId: $preview, expectedEnvironmentId: $environment,
ifMatchPreviewVersion: $previewVersion,
ifMatchEnvironmentVersion: $environmentVersion, follow: true, tailLines: 100
) { podName container timestamp message stream }
}
mutation PreviewExport($input: ExportAppLogsInput!) {
exportAstroliftAppLogs(input: $input) {
ok errors { code message field }
data { id status downloadUrl sha256 byteCount rowCount truncated expiresAt }
}
}
Supply the export input with appSlug, podName, format (CSV, NDJSON or
TXT) and all four selectors from the exact preview read. An optional
environmentName must equal that read's canonical name. Single-pod live logs
also require podName; plural live logs discover replicas only inside the
reviewed namespace. Explicit pod, workload and container selections must match
current discovery inside that namespace before log bytes open. These checks use
existing provider pod metadata; they do not provide an atomic Kubernetes pod-UID
lease or prove future pods by name alone.
All four selectors must be present together. Missing proof for a name or route
that identifies an actual preview is refused. Partial proof, changed versions,
foreign ownership, deleted/replaced bindings, retirement, and invalid placement
return a static PRECONDITION without trying the production/default cluster.
Ordinary app log requests retain their existing behavior. Refresh exact metadata
and start an explicitly reviewed new request after a refusal; never silently
replace the target or resume an old stream onto a replacement environment.
Exact streams recheck active requester membership, live credential expiry and
revocation, current grants and operation policies, and the entire captured
app/environment/cluster binding before provider setup, before each emitted line,
and while idle at one-second intervals. Refusal closes underlying streams and
returns a static GraphQL error (PRECONDITION or PERMISSION_DENIED). This is
periodic authorization, not a permanent lease or an atomic provider-write fence.
Exact exports cap the existing line count and add a 30-second source-read deadline;
they perform the same checks during collection and before publishing a receipt.
Failed or refused reads do not produce a successful artifact receipt.
Preview artifact receipts retain reviewed identity/version and original authority metadata alongside the existing log artifact storage. They retain no token value or copied log body in the receipt. Their opaque download URL and TTL are necessary but insufficient:
- A same-origin browser must use the original requester's authenticated session.
- For an export created with a bearer credential, a bearer client must send that same original credential. A different token, even owned by the same user, cannot widen access. A bearer cannot substitute for a session-created export.
- Both paths recheck the requester, present grants, original credential scopes and team ceiling, live token validity when one created the export, exact source lifecycle and versions, artifact status and TTL. Browser use never bypasses the original token's revocation or scope ceiling. Any selected organization must match the artifact's original organization.
Preview downloads use Cache-Control: private, no-store so shared caches do not
serve a previous authenticated response as fresh authorization. The download
view returns opaque 404 on link, source or access refusal. An
invalid or expired bearer can instead be rejected by the shared authentication
middleware with generic 401 before the view runs. Each 64-KiB artifact chunk is
checked before reading under ASGI; mid-transfer refusal closes the file and
truncates the response. A client must verify Content-Length and
X-App-Log-Export-Sha256 rather than treat a partial transfer as success. The link
never reruns a provider query. Old receipts remain empty and gain no inferred
preview proof; a legacy artifact whose recorded environment currently identifies
a preview is refused, including a recorded name still associated with a
soft-deleted preview. Ordinary legacy capability-link exports retain their
existing token-and-TTL behavior. Older receipts stored an environment name rather
than immutable source proof: renaming or reusing that name can leave the original
source unclassifiable. Such artifacts retain legacy link behavior until expiry;
this contract cannot retroactively prove or secure their original preview source.
These server contracts do not add a live WebSocket or export command to the CLI.
The current astro app previews logs command uses guarded historical GraphQL
polling, carrying all four selectors on every page and follow request. The web
preview detail also uses the existing historical query. API clients can consume
the new live/export fields explicitly; native mobile acceptance remains separate.
Local PostgreSQL/provider-boundary tests establish source and authorization
contracts, not successful log delivery from a live production preview.
Web and CLI¶
The web detail route reads one GUID rather than finding it in a capped catalog. Resources, logs, and deployment history each have explicit load controls. A new actor, organization, preview, or target hides the prior scope's data, and late responses cannot populate the new detail.
The matching CLI supports:
astro app previews show example-app --id PREVIEW_GUID --json
astro app previews show example-app --id PREVIEW_GUID --resources --json
astro app previews logs example-app --id PREVIEW_GUID --since 30m --tail 200
astro app previews open example-app --id PREVIEW_GUID --url
--id reads directly, including previews older than the recent 200-entry catalog.
--pr and --branch remain bounded discovery selectors and are followed by an
exact GUID reread. Logs carry the stored environment GUID and reviewed versions
on every page. Open and logs refuse retired or unavailable bindings. Upgrade the
server and CLI together: the matching CLI requires the resource-only preview
schema and reports an explicit error against an older server.
Public capability metadata exposes previews.exact_identity,
previews.reviewed_routes, previews.explicit_runtime_resources,
previews.reviewed_live_logs, and previews.reviewed_log_exports. Discovery is
informational and never grants permission. Mobile clients can consume this
canonical contract; native mobile integration and consent UI require
separate acceptance evidence.
The resource-only contract is pinned to merged backend 09e3f2c2. Its operator rollout notes cover paired clients and worker retirement. Source qualification does not prove that the selected installation has deployed this contract.