1.0.0-beta.9 · exact release
Fastly config, secrets, KV, and backends
Source fda16de1b352 · Published v1.0.0-beta.9
User code stays provider-neutral:
import { grip } from '@pulse-compute/grip'import { Pulse } from '@pulse-compute/pulse'
interface User { id: number name: string}
interface Session { userId: number}
const app = new Pulse({ auto: true })
// Config, secret, and a named Fastly backend.app.get('/users/7', async (ctx) => { const base = await ctx.config.get('API_BASE') const token = await ctx.secret.get('API_TOKEN') const user = await ctx .fetch('https://api.example.com/users/7', { headers: { authorization: `Bearer ${token}`, accept: 'application/json', 'x-api-base': base, }, }) .json<User>() return ctx.json({ user })})
// Named Fastly KV storage.app.get('/session', async (ctx) => { const current = await ctx.kv<Session>('sessions').get('session:123') if (current === undefined) { return ctx.json({ error: 'not_found' }, { status: 404 }) } await ctx.kv<Session>('sessions').put('session:last', current) return ctx.json({ session: current })})
// Request-bound GRIP broadcast through the supported package root.app.post('/publish', async (ctx) => { const acknowledgement = await grip.broadcast(ctx, { channel: 'events:demo', event: 'pulse.message', id: 'message-1', data: { message: 'hello from Pulse' }, }) return ctx.json( { accepted: acknowledgement.accepted }, { status: 202 }, )})
export default appFastly bindings belong in project configuration:
import { defineConfig } from '@pulse-compute/pulse'
export default defineConfig((scope) => ({ pulse: { entry: 'src/index.ts', tests: 'tests/pulse.harness.ts', defaultProfile: 'local', strict: true, }, local: { host: 'fastly', target: 'native', outDir: 'dist', apiBase: scope.config('API_BASE'), apiToken: scope.secret('API_TOKEN'), dev: { config: { API_BASE: 'https://api.example.com' }, secrets: { API_TOKEN: 'local-example-secret', GRIP_TOKEN: 'local-grip-secret', }, kv: { sessions: { 'session:123': { userId: 123 } }, }, fetches: { 'https://api.example.com/users/7': { value: { id: 7, name: 'Ada' }, }, 'POST https://publisher.example.com/publish': { status: 202, value: { accepted: true, messageId: 'message-1' }, }, }, }, fastly: { bindings: { configStore: 'app_config', secretStore: 'app_secrets', kv: { sessions: 'app_sessions' }, backends: { 'https://api.example.com': 'api_backend', 'https://publisher.example.com': 'publisher_backend', }, dynamicBackends: false, grip: { publishEndpoint: 'https://publisher.example.com/publish', publishBackend: 'publisher_backend', authentication: { scheme: 'bearer', secretRef: 'GRIP_TOKEN', }, }, }, build: { name: 'pulse-fastly-capabilities-example' }, }, },}))The same example maps its logical sessions store and its GRIP publish endpoint
inside the provider-owned Fastly configuration boundary:
examples/05-fastly-capabilities.
Dynamic backends are disabled by default. Map every known origin to a named backend or opt in deliberately.
Secret values are never included raw in trace output, errors, snapshots, or CLI project JSON.
Fastly Native conditional KV reads and writes redact object field names as
complete quoted JSON tokens in managed logs. A field named id therefore does
not alter unrelated structured metric keys such as considered or invalidAcks,
and numeric fields remain numeric. KV string values, lookup keys, generation
tokens, and known Secret Store values retain substring redaction. Keep operational
observations separate from private payloads: redacting a payload does not preserve
its schema or distinguish fields that collapse to the same redacted name.
Fastly Native ctx.log writes redacted, newline-terminated messages to standard
output through WASI fd_write (descriptor 1). Each enabled statement makes one
best-effort write; errors and partial writes do not retry, change the HTTP result,
or disable later statements. It does not open a named logging endpoint: stdout
and stderr are reserved names. No logging binding is required.
This corrects the beta.8 Native logger's reserved-endpoint lookup. Local Viceroy
qualification requires positive arrival/outcome output as well as secret absence.
After deploying a corrected candidate, verify delivery with
fastly log-tail --stream stdout before relying on live cadence reporting.
Local and injected-host checks do not establish deployed log-tail delivery.
The opt-in fastly-stdio-installed task packs and installs exact candidates outside
the checkout with lifecycle scripts disabled. It checks installed bytes before
and after compilation, replays the emitted Wasm with failing writes, and requires
an authenticated probe's arrival/outcome logs from Viceroy:
PULSE_VICEROY_BIN=/path/to/viceroy \node wasm/scripts/run-wasm-tests.cjs --task fastly-stdio-installed --no-reportFor target: 'native', pulse build compiles the generated Compute package to
direct-host-ABI bin/main.wasm. For target: 'javascript', it emits the
deterministic source/deployment closure consumed by the pinned Fastly JavaScript
runtime compiler. Both targets use these same logical bindings.
The external reality task records its selected launcher and executes the Native candidate through either direct Viceroy:
PULSE_VICEROY_BIN=/path/to/viceroy \node wasm/scripts/run-wasm-tests.cjs --task provider-fastly-compute-reality --no-reportor fastly compute serve --file selected with PULSE_FASTLY_BIN. The gate
sends real HTTP requests through the Native module. It remains separate from
portable and offline JavaScript-candidate validation and does not deploy a
service.
Experimental Native binary forwarding
Section titled “Experimental Native binary forwarding”Opt in with fastly.bodyForwarding: { requestMaxBytes: 1048576, responseMaxBytes: 1048576 } and fastly.maxDurationMs: 2000. In a Native
application with one fetch effect, return
ctx.fetch('https://origin.example/upload', { method: 'POST', body: ctx.req.body() })
from the handler and map that origin to a named backend. Both byte bounds are
required, independent, and limited to 1 MiB. Request headers must be literal,
explicit and fit 64 pairs/16 KiB; incoming credentials are not copied implicitly.
Pulse pumps opaque bytes, watches for early origin responses and enforces the
request deadline plus any earlier fetch timeoutMs through response pumping.
Successful final close hands queued delivery to Fastly; it provides no client
receipt or post-handoff cancellation. Fastly JavaScript remains ineligible.
See body transport limits
for the initial shape restrictions and qualification boundary.
fastly-incoming-body-forwarding checks the normal Native build and adversarial
host ABI cases. Set PULSE_VICEROY_BIN to the reviewed Viceroy 0.21.1 executable
to also check real HTTP byte identity, caps, incremental upload/output, early
responses, header filtering, bodyless statuses, disconnects and post-header
aborts. The separate installed task requires that executable:
PULSE_VICEROY_BIN=/path/to/viceroy \node wasm/scripts/run-wasm-tests.cjs --task fwd9-03-installed --no-reportIt packs exact candidates, installs outside the checkout with lifecycle scripts disabled, verifies installed bytes before and after the CLI workflows, and replays the emitted Wasm under both the ABI fixture and Viceroy. Its report binds package, Wasm, runner and engine identities. Additional builds exercise unequal request/response caps and an earlier total deadline. These are local and installed qualification results; deployed Fastly remains unqualified. Fixed guest storage and loopback timing samples do not prove a host queue bound, zero-copy forwarding or a Pulse-versus-Rust performance result.