# W7S Source: https://w7s.io/docs/ W7S is an [open source](https://github.com/w7s-io/w7s-core) deployment platform managed directly from GitHub Actions workflows. The same W7S core can power [other deployment clouds](https://w7s.io/docs/self-host/). The core idea is simple: 1. Put an app in a GitHub repository. 2. Add the W7S GitHub Action. 3. Push to GitHub. 4. W7S verifies the GitHub token, receives the deploy archive, and serves the app. ## Minimal workflow ```yaml name: Deploy on: push: branches: - main workflow_dispatch: permissions: contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` ## What W7S can deploy - Static frontends built into `dist/`, `dist/client/`, `build/`, `out/`, or `frontend/dist/`. - [`w7s-io/example-static-site`](https://github.com/w7s-io/example-static-site) - Native backend code from `backend/` or `worker/`, authored or built as JavaScript or TypeScript runtime modules. - [`w7s-io/example-native-backend`](https://github.com/w7s-io/example-native-backend) - Fullstack apps that include both a JavaScript/TypeScript backend root and a static frontend root. - [`w7s-io/example-fullstack-ts`](https://github.com/w7s-io/example-fullstack-ts) - Stateful object bindings for JavaScript/TypeScript native backends. - [`w7s-io/example-durable-counter`](https://github.com/w7s-io/example-durable-counter) - Serverless DBs for JavaScript/TypeScript native backends. - [`w7s-io/example-serverless-database`](https://github.com/w7s-io/example-serverless-database) - Postgres bindings for external databases. - [`w7s-io/example-postgres-binding`](https://github.com/w7s-io/example-postgres-binding) - W7S-provided AI service bindings for JavaScript/TypeScript native backends. - [`w7s-io/example-ai-joke`](https://github.com/w7s-io/example-ai-joke) - Backend-to-backend RPC through internal service bindings. - [`w7s-io/example-rpc-client`](https://github.com/w7s-io/example-rpc-client) - [`w7s-io/example-rpc-datetime`](https://github.com/w7s-io/example-rpc-datetime) - Background queues delivered to JavaScript/TypeScript native backends. - [`w7s-io/example-queue-worker`](https://github.com/w7s-io/example-queue-worker) - [`w7s-io/example-queue-producer`](https://github.com/w7s-io/example-queue-producer) - [`w7s-io/example-queue-consumer`](https://github.com/w7s-io/example-queue-consumer) - Cron schedules delivered to JavaScript/TypeScript native backends. - [`w7s-io/example-schedules`](https://github.com/w7s-io/example-schedules) - Durable workflow instances delivered to JavaScript/TypeScript native backends. - [`w7s-io/example-workflows`](https://github.com/w7s-io/example-workflows) - Daily usage rollups, hourly platform usage sync, app suspension, warning thresholds, and daily limits. - [`w7s-io/example-usage-check`](https://github.com/w7s-io/example-usage-check) - Platform event analytics exposed through an authenticated API. - [`w7s-io/example-logs`](https://github.com/w7s-io/example-logs) - Backend console and exception logs exposed through an authenticated API. - [`w7s-io/example-logs`](https://github.com/w7s-io/example-logs) - Custom domains declared with a [`CNAME`](https://w7s.io/docs/custom-domains/) file. - [`w7s-io/example-fullstack-ts`](https://github.com/w7s-io/example-fullstack-ts) ## W7S vs Others | Feature | W7S | Vercel | Cloudflare Pages + Workers | Railway / Fly.io | | --- | --- | --- | --- | --- | | GitHub-native deployment | Yes (one Action) | Good | Manual | Good | | Open Source + Self-hostable | Yes | No | Partial | No | | Native JS/TS Backends | Yes | Serverless Functions | Workers | Yes | | Serverless DB | Included | Add-on | Native DB | External | | External Postgres | Supported | Yes (paid) | Manual | Yes | | Queues, Cron & Workflows | Native | Limited | Yes | Yes | | Vendor Lock-in | None | High | Medium | High | | Pricing | Free self-host + hosted | Usage-based | Usage-based | Usage-based | Continue with [Deploy From GitHub](./deploy-from-github.md), then add the [daily quota check recommendation](./recommendations.md). To run your own W7S cloud on a Cloudflare account and domain you control, see [Self Host W7S](./self-host.md). --- # Deploy From GitHub Source: https://w7s.io/docs/deploy-from-github/ W7S deploys are authorized with the repository's GitHub token. If the workflow token can read the repository, it can deploy that repository to W7S. ## Add the workflow Create `.github/workflows/deploy.yml`: ```yaml name: Deploy on: push: branches: - main workflow_dispatch: permissions: contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` The action packages the repository, sends it to `https://w7s.cloud/api/v1/deploy`, and includes the repository, branch, and commit metadata. Push and manual runs deploy the repo. For a separate daily workflow that checks quota and free-tier limits without deploying, see [Recommendations](./recommendations.md). By default, pushes from `main` and `master` deploy to the `production` environment. Pushes from other branches deploy to a branch environment and are served from `https://--.w7s.cloud//`. W7S lowercases the branch name, replaces runs of characters outside `a-z`, `0-9`, and `-` with `-`, collapses repeated hyphens, trims leading/trailing hyphens, and caps the result at 63 characters. For example, `feature/API.v2_test` becomes `feature-api-v2-test`. If the repo contains `w7s.json`, the action also collects declared `vars` and `secrets` from the workflow environment and passes them as backend bindings. After a successful deploy, the action checks that repo's W7S usage for the deployed day. If any daily limits are near or over the effective policy, or W7S has suspended the app after hourly platform usage sync, it adds a warning section to the GitHub Actions summary and opens or updates a single GitHub issue for that repo/environment. Requests that would exceed a daily limit return HTTP `429`. `issues: write` is only used for W7S usage warning issues. If you want summary-only warnings, remove that permission and set: ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} usage-warnings-issue: false ``` ## Telegram notifications The W7S Telegram bot can tell you the `telegram-chat-id` to add to the deploy action. Start a chat with the bot and send `/start`; it replies with copyable code blocks for the chat id and a complete workflow example. The reply looks like this: ````text W7S Telegram notifications Use this chat id in your GitHub Actions workflow: ``` telegram-chat-id: "123456789" ``` Example: ``` name: Deploy on: push: workflow_dispatch: permissions: contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} telegram-chat-id: "123456789" telegram-events: deploy_success,deploy_warning,deploy_error,app_suspended,payment_request ``` ```` Add that chat id to the deploy step: ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} telegram-chat-id: "123456789" telegram-events: deploy_success,deploy_warning,deploy_error,app_suspended,payment_request ``` W7S links the chat id to the repo/environment after a successful authenticated deploy. That lets W7S send deploy notifications, app suspension alerts, and future payment-request notifications for that repo. ## Build before deploy W7S does not run your app build for you. Build in GitHub Actions before calling the W7S action. Example for a frontend that builds to `dist/`: ```yaml steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm run build - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` ## Deploy a subdirectory If your deployable output lives in a staging directory, use `working-directory`: ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} working-directory: build ``` This is useful for static documentation sites and other generated artifacts. ## Pass runtime values Declare values in `w7s.json`: ```json { "vars": ["PUBLIC_API_KEY"], "secrets": ["PRIVATE_API_KEY"] } ``` Then expose them to the deploy step: ```yaml - uses: w7s-io/w7s-cloud@v1 env: PUBLIC_API_KEY: ${{ vars.PUBLIC_API_KEY }} PRIVATE_API_KEY: ${{ secrets.PRIVATE_API_KEY }} with: token: ${{ github.token }} ``` W7S exposes those names on the backend `env` object: ```ts title="backend/index.ts" type Env = { PUBLIC_API_KEY?: string; PRIVATE_API_KEY?: string; }; export default { async fetch(_request: Request, env: Env): Promise { const response = await fetch("https://api.example.com/data", { headers: { "x-api-key": env.PRIVATE_API_KEY ?? "" } }); return Response.json({ publicApiKeyConfigured: Boolean(env.PUBLIC_API_KEY), privateApiKeyConfigured: Boolean(env.PRIVATE_API_KEY), upstreamOk: response.ok }); } }; ``` The property names match the entries in `vars` and `secrets`. Secrets are available to backend code, but should not be returned to browsers or logs. --- # Self Host W7S Source: https://w7s.io/docs/self-host/ W7S is open source. The hosted `w7s.cloud` service is one deployment of the W7S core Worker, and you can run the same core on your own Cloudflare account and domain. Self-hosting gives you the same deployment model: ```text https://.// ``` The public hosted service uses: ```text https://.w7s.cloud// ``` Your self-hosted cloud uses the same GitHub-token authorization model, the same `w7s-io/w7s-cloud@v1` deploy action, and the same app repository layout. ## What You Run A W7S cloud is one Cloudflare Worker plus Cloudflare account resources: - a public W7S core Worker that serves the landing page, API, static assets, and runtime router; - one Workers for Platforms dispatch namespace for native backend Workers; - one KV namespace for deployment metadata, manifests, usage, logs, limits, and app records; - one FS bucket for deployed static frontend assets; - one Cloudflare Workflow binding for app workflow dispatch; - optional Analytics Engine dataset for platform event analytics; - optional Telegram bot secrets for platform manager and repo notifications. The core deploy workflow creates or reuses the KV namespace, FS bucket, and dispatch namespace. DNS records are intentionally manual so the domain owner stays in control. ## Requirements You need: - a Cloudflare account; - a Cloudflare zone you control, such as `example.com`; - Workers for Platforms available on that Cloudflare account; - a GitHub repo forked from [`w7s-io/w7s-core`](https://github.com/w7s-io/w7s-core); - a Cloudflare API token available to the fork's GitHub Actions workflow; - the ability to create DNS records in the Cloudflare zone. The current core expects `W7S_ZONE_NAME` to be a Cloudflare zone name. Use a dedicated zone like `example.com` or `w7s.example.com` only if that subdomain is onboarded as its own Cloudflare zone. ## 1. Fork The Core Fork the core repo: ```text https://github.com/w7s-io/w7s-core ``` The fork keeps the included workflow: ```text .github/workflows/deploy.yml ``` That workflow installs dependencies, runs checks, generates a Wrangler config from Cloudflare API state, deploys the core Worker, and reconciles Worker routes. ## 2. Create A Cloudflare API Token Create a Cloudflare API token for the account that owns the zone. The token needs enough access to: - deploy Workers; - manage Workers routes for the W7S zone; - read zones; - create and read KV namespaces; - create and read FS buckets; - create, read, and query managed DB resources for app bindings; - create and read Workers for Platforms dispatch namespaces; - publish scripts into the dispatch namespace; - deploy Cloudflare Workflows attached to the core Worker. If you want deployed apps to claim custom domains under other Cloudflare zones, the same token also needs zone read and Workers route permissions for those zones. ## 3. Set GitHub Secrets In your core fork, add these repository secrets: ```text CLOUDFLARE_API_TOKEN CLOUDFLARE_ACCOUNT_ID ``` Optional Telegram manager notification secrets: ```text W7S_TELEGRAM_BOT_TOKEN W7S_TELEGRAM_CHAT_ID W7S_TELEGRAM_WEBHOOK_SECRET ``` The manager chat id is the chat, group, or channel the bot should message. For a private chat, send `/start` to the bot before using the chat id. ## 4. Set GitHub Variables At minimum, set the base domain and enable the wildcard route: ```text W7S_ZONE_NAME=example.com W7S_ATTACH_WILDCARD_ROUTE=true ``` Optional variables let you change resource names and runtime limits: ```text W7S_DEPLOYMENTS_KV_NAME=w7s-io-deployments W7S_STATIC_ASSETS_BUCKET=w7s-io-static-assets W7S_DISPATCH_NAMESPACE=w7s-isolate W7S_ANALYTICS_DATASET=w7s_platform_events W7S_WORKFLOW_NAME=w7s-workflows W7S_USER_WORKER_CPU_MS=50 W7S_USER_WORKER_SUBREQUESTS=25 W7S_TELEGRAM_EVENTS=all ``` Use different names if the Cloudflare account already has resources with the defaults. ## 5. Configure DNS Create a proxied wildcard record in the Cloudflare zone: ```text Type: CNAME Name: * Target: example.com Proxy status: Proxied TTL: Auto ``` This makes `guerrerocarlos.example.com`, `w7s-io.example.com`, and every other owner host reach the core Worker. The core deploy attaches the Worker to: ```text example.com *.example.com/* ``` The apex host can serve the default W7S page or a same-name GitHub repo. Owner subdomains serve deployed apps. ## 6. Deploy The Core Run the core repo's `Deploy` workflow from GitHub Actions, or push to `main`. The workflow runs: ```sh npm ci npm run check npm run prepare:cloudflare npx wrangler deploy --config wrangler.generated.jsonc --secrets-file .wrangler/secrets.json npm run reconcile:cloudflare-routes ``` `npm run prepare:cloudflare` creates or reuses: - the `DEPLOYMENTS_KV` namespace; - the `STATIC_ASSETS` FS bucket; - the `DISPATCHER` Workers for Platforms dispatch namespace; - the generated Wrangler config used for the deploy. Validate the deployment: ```sh curl https://example.com/health ``` A healthy response includes the core commit, branch, and deployment timestamp. ## 7. Deploy Apps To Your Cloud In an app repo, point the official action at your self-hosted deploy API: ```yaml name: Deploy on: push: branches: - main workflow_dispatch: permissions: contents: read issues: write jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} deploy-url: https://example.com/api/v1/deploy ``` After deployment, a repo such as `github.com/acme/api` is served at: ```text https://acme.example.com/api/ ``` Non-production branches are served from branch-prefixed hosts: ```text https://feature-login--acme.example.com/api/ ``` The branch prefix is the sanitized W7S environment name. W7S lowercases the branch, replaces runs of characters outside `a-z`, `0-9`, and `-` with `-`, collapses repeated hyphens, trims leading/trailing hyphens, and caps it at 63 characters. ## Custom Domains Apps can still declare custom domains with a [`CNAME`](https://w7s.io/docs/custom-domains/) file: ```text app.customer.com ``` The domain owner must point DNS at your W7S cloud, usually: ```text Type: CNAME Name: app Target: example.com Proxy status: Proxied TTL: Auto ``` For stronger ownership control, add a TXT allowlist at the custom domain zone: ```text Name: _w7s.customer.com Value: acme/api ``` TXT values can be comma-separated and may allow a whole GitHub owner or one repo: ```text acme acme/api,partners ``` ## Operations Self-hosting means you own the Cloudflare bill and the operational policy. Keep these in place from day one: - daily usage limits and burst limits; - `W7S_USER_WORKER_CPU_MS` and `W7S_USER_WORKER_SUBREQUESTS` caps; - short log retention with `W7S_LOG_RETENTION_SECONDS`; - `W7S_ANALYTICS_DATASET` for platform events, if you want queryable history; - the daily GitHub Actions quota-check workflow for app repos; - Telegram manager notifications for deploy warnings, deploy errors, app suspensions, and usage collection failures. Because deploys are still authorized with the app repo's GitHub token, you do not need a separate W7S account system for app owners. If the workflow token can read `owner/repo`, it can deploy `owner/repo` to your W7S cloud. --- # Recommendations Source: https://w7s.io/docs/recommendations/ ## Daily quota check Add a separate GitHub Actions workflow that runs once per day to check W7S usage limits without deploying the app. Create `.github/workflows/w7s-usage-check.yml`: ```yaml name: W7S Usage Check on: workflow_dispatch: schedule: - cron: "17 9 * * *" permissions: contents: read issues: write jobs: check: runs-on: ubuntu-latest steps: - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} usage-check-only: true ``` This workflow does not check out the repo, install dependencies, build, package, or deploy. It only asks W7S for the current day's usage for the repository, including hourly platform-synced direct resource usage when available. If the app is near or over a quota or free-tier limit, or W7S has suspended the app until the next UTC day, `w7s-io/w7s-cloud@v1` writes a GitHub Actions summary and opens or updates one GitHub issue for the repo/environment. `issues: write` is only needed so the workflow can create or update that warning issue. For non-production environments, pass the W7S environment explicitly: ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} usage-check-only: true environment: staging ``` Explicit environment values use the same DNS-safe normalization as branch deployments. See [Usage Accounting](./usage-accounting.md) for the tracked metrics, current limits, and hard enforcement behavior. --- # Security Source: https://w7s.io/docs/security/ This page is for developers deploying frontend and backend applications on W7S. W7S Cloud serves organization deployments from `*.w7s.cloud`. Each subdomain is a separate organization tenant. Tenants are mutually untrusted, even though they share the same parent domain. ```text w7s.cloud marketing/root .w7s.cloud organization tenant ``` Do not treat sibling subdomains as trusted. `acme.w7s.cloud` and `example.w7s.cloud` belong to different GitHub organizations and must be handled as separate security boundaries. ## Tenant isolation Use the exact origin as the trust boundary: ```text https://.w7s.cloud ``` Avoid designs that rely on the shared parent domain: - Do not use parent-domain cookies for auth. - Do not trust requests because they came from another `*.w7s.cloud` subdomain. - Do not share browser storage assumptions across tenants. - Do not build authorization logic that accepts every `*.w7s.cloud` origin. - Do not mix unrelated applications with different trust levels on the same tenant hostname unless they are designed to share the same users and storage. - For production custom-domain apps, consider `routing.defaultDomain=false`. If your application needs admin pages, API routes, previews, or uploads, prefer tenant-owned paths: ```text https://.w7s.cloud/admin/ https://.w7s.cloud/api/ ``` For scriptable uploaded content, use a separate content domain when possible. For production apps with a custom domain, consider disabling the default `w7s.cloud` route so the app has one canonical origin: ```json title="w7s.json" { "routing": { "defaultDomain": false } } ``` This requires a [`CNAME`](https://w7s.io/docs/custom-domains/) custom domain and prevents the same deployment from also being served from `https://.w7s.cloud/...`. ## Cookie leakage Cookie leakage can happen when a cookie is scoped to the shared parent domain. Avoid: ```http Set-Cookie: session=...; Domain=.w7s.cloud; Path=/ Set-Cookie: session=...; Domain=w7s.cloud; Path=/ ``` Those cookies can be sent to sibling tenant subdomains. Use host-only cookies: ```http Set-Cookie: session=...; Path=/; Secure; HttpOnly; SameSite=Lax ``` For session cookies and other sensitive browser state, prefer the `__Host-` prefix: ```http Set-Cookie: __Host-session=...; Path=/; Secure; HttpOnly; SameSite=Lax ``` `__Host-` cookies must be secure, must use `Path=/`, and must not include a `Domain` attribute. That makes them bound to the exact tenant hostname. If you use `routing.defaultDomain=false`, set cookies only for your custom domain origin. That reduces confusion around which hostname owns sessions, redirects, storage, and CSRF checks. ## Cookie bombing Cookie bombing happens when a browser sends too many or too-large cookies, causing requests to exceed header limits or break your backend routes. Best practices: - Never set cookies for `.w7s.cloud`. - Keep cookie values small. - Reject oversized `Cookie` headers in your backend before doing expensive work. - Keep an allowlist of expected cookie names when your app controls them. - Log and investigate unusually large cookie headers or cookie counts. Recommended backend check: ```js const cookie = request.headers.get("cookie") || ""; if (cookie.length > 8 * 1024) { return new Response("Cookie header too large", { status: 431 }); } ``` If you can allowlist cookies, keep it small: ```text __Host-session __Host-csrf tenant_pref ``` ## XSS Cross-site scripting usually comes from application code, not the hosting platform. Treat all user input as untrusted. Best practices: - Escape output by default. - Avoid direct HTML injection APIs. - Sanitize user-provided HTML with a real sanitizer. - Avoid scriptable uploads on authenticated app origins. - Keep dependencies current. - Review AI-generated code for unsafe HTML, markdown, redirects, and scripts. Use a baseline Content Security Policy: ```http Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none' X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Strict-Transport-Security: max-age=31536000; includeSubDomains ``` For existing apps, start with CSP report-only mode, review violations, then enforce the policy. If your backend returns HTML, set these headers from your backend response. If your frontend is static, configure your framework or build output to send the headers where supported. ## Storage poisoning Browser storage is scoped to the exact origin, so these are separate: ```text https://acme.w7s.cloud https://example.w7s.cloud ``` The main risk is trusting browser storage too much or letting untrusted code run on the same tenant origin. Best practices: - Treat `localStorage`, `sessionStorage`, and IndexedDB as user-controlled input. - Validate and version stored values before using them. - Do not store long-lived auth tokens in browser storage. - Do not host mutually untrusted apps on the same tenant hostname. - Clear or migrate old storage keys when changing auth or tenant behavior. Prefer HttpOnly cookies for sessions: ```http Set-Cookie: __Host-session=...; Path=/; Secure; HttpOnly; SameSite=Lax ``` ## Origin and CSRF checks If your app uses browser cookies, state-changing requests should validate the request origin and CSRF token. For mutating methods in your backend: ```js const mutates = ["POST", "PUT", "PATCH", "DELETE"].includes(request.method); const origin = request.headers.get("origin"); const expectedOrigin = new URL(request.url).origin; if (mutates && origin && origin !== expectedOrigin) { return new Response("Invalid origin", { status: 403 }); } ``` Do not allow requests just because the origin ends in `.w7s.cloud`. For automation APIs, prefer bearer tokens or signed requests instead of browser cookies. ## Uploaded content Uploaded files are risky when they can execute scripts or be interpreted as HTML, SVG, JavaScript, or other active content. Best practices: - Serve untrusted uploaded content from a separate content domain when possible. - Do not attach auth cookies to uploaded-content responses. - Force downloads for risky MIME types. - Use `X-Content-Type-Options: nosniff`. - Use a restrictive CSP for content responses. - Do not render user uploads inside privileged app pages unless sanitized. ## Secrets and GitHub workflows W7S deploys from GitHub Actions, so repository and workflow security matter. Best practices: - Keep GitHub repository permissions tight. - Use the least permissions needed in workflow `permissions`. - Rotate exposed credentials immediately. - Do not commit API keys, tokens, private keys, or production secrets. - Review pull requests before deploying them to trusted environments. - Keep dependency lockfiles and build scripts reviewable. Minimal deploy permissions: ```yaml permissions: contents: read ``` Only add broader permissions when a workflow needs them. ## Monitoring Log security-relevant events from your application: - attempts to set or use parent-domain cookies - oversized `Cookie` headers - excessive cookie counts - CSP violations - rejected XSS, injection, or unsafe upload attempts - mutating requests with unexpected origins - uploaded-content responses attempting to set cookies - repeated auth failures - unusual traffic spikes These signals help detect tenant isolation problems, application bugs, abuse, and misconfiguration before they become larger incidents. ## Checklist Before treating a deployment as production-ready: - Use host-only `__Host-` cookies for sessions. - Do not set `Domain=.w7s.cloud` or `Domain=w7s.cloud`. - Use `routing.defaultDomain=false` when production traffic should only use a custom domain. - Reject oversized cookie headers in backend routes. - Add a CSP and `nosniff`. - Validate exact origins on mutating browser requests. - Use CSRF protection when browser cookies authorize writes. - Keep auth tokens out of browser storage. - Treat browser storage as untrusted. - Isolate scriptable uploaded content. - Review GitHub Actions permissions and secrets. - Monitor security and abuse signals. --- # Pricing Source: https://w7s.io/docs/pricing/ [w7s.cloud](https://w7s.cloud/) is free to start. You do not need a W7S account, a credit card, or a separate cloud setup to deploy through the hosted service. The hosted service is still intentionally simple: small apps can deploy, test, demo, and share before billing matters. When an app starts getting meaningful traffic, the model should be usage-based instead of a mandatory subscription just to keep the app online. The serverless DB is part of that model. If an app uses the [W7S serverless database](./serverless-database.md), database reads, writes, and stored data are included in the W7S usage estimate instead of requiring a separate database bill to understand the real cost shape. External Postgres is still possible when an app needs it. ## How To Read This The calculator estimates monthly W7S usage from traffic, storage, queues, logs, and stateful backend features. It also includes W7S platform overhead for routing, usage enforcement, usage counters, log handling, and other control-plane work. It is useful for planning, quota design, and understanding which features multiply cost. It is not a final hosted [w7s.cloud](https://w7s.cloud/) bill. Pick a startup stage first. Each stage sets the sliders to a common traffic and infrastructure shape, and every slider uses whole-number values. The `Free tier` stage is the included baseline and should estimate `$0.00`. Higher stages show billable overage above that baseline. The estimate subtracts the included baseline first, estimates operating cost from the selected traffic shape, adds W7S overhead, then applies a 50% gross margin to the billable overage. ## What Drives Cost | W7S area | Main cost driver | Why it matters | | --- | --- | --- | | Public traffic | Runtime requests and CPU | W7S routes requests through the platform and app runtime. More requests and heavier handlers increase usage. | | Static frontends | Asset storage and origin reads on cache misses | W7S stores deploy assets and caches immutable files. Better cache hit rates reduce origin reads. | | Native backends | CPU milliseconds, storage reads/writes, logs | Expensive handlers, chatty data access, and noisy logging increase cost faster than simple request count. | | Serverless DB and FS | Operation counts and stored GB-month | W7S can provision per-app DB, key-value, and FS bindings; database usage is included in the same usage model. | | Queues | Operations per message | A normal delivered queue message is roughly one write, one read, and one delete. Retries add more reads. | | Stateful coordination | Requests, active duration, and storage | Long-lived stateful workloads can dominate cost even if request volume is low. | | Logs | Events written | W7S exposes app console and exception logs, but log volume should stay intentional. | | W7S platform overhead | Routing, usage guards, counters, and log ingestion | W7S runs extra platform work around each app so deploys, public routing, quota enforcement, and observability keep working. | ## Pricing Notes The calculator is a planning tool. It keeps provider details out of the user-facing estimate and shows W7S usage categories instead of infrastructure product names. The final estimate includes a 50% gross margin after W7S overhead, so the public price target is higher than raw infrastructure cost. ## Free-Tier Policy W7S also has its own free-tier guardrails. These are not billing-grade counters yet; they are operational protections that keep one app, owner, or global account from burning through shared infrastructure unexpectedly. Use the [Usage Accounting](./usage-accounting.md) API to inspect daily usage, warning thresholds, daily caps, hourly resource usage, and the GitHub issue workflow that reports quota pressure back to the repository. --- # Project Layouts Source: https://w7s.io/docs/project-layouts/ W7S detects deploy targets from the archive uploaded by GitHub Actions. ## Static frontends W7S serves static assets from the first matching output root: ```text frontend/dist/ dist/client/ dist/ build/ out/ ``` Each static root should include an `index.html`. One exception is framework SSR output. Frameworks such as TanStack Start may build frontend assets into `dist/client/` without an `index.html`, while rendering HTML from `dist/server/index.js`. W7S supports that paired layout: ```text dist/server/index.js dist/client/assets/app.js ``` ## Native backends Native backends must be JavaScript or TypeScript runtime modules. W7S publishes native backend code from either: ```text backend/index.ts backend/index.js worker/index.ts worker/index.js ``` `index.mts` and `index.mjs` are also supported. Other backend languages are not uploaded directly. Build or bundle them to one of the supported JavaScript/TypeScript entrypoints before deploying. Native backend modules must use local relative imports. If the backend depends on npm packages, bundle it in CI and upload the bundled backend files. Framework SSR builds can also publish their generated JavaScript server entrypoint: ```text dist/server/index.js ``` When an SSR build emits runtime compatibility metadata, W7S includes supported flags such as `nodejs_compat` when uploading the backend module. Uploaded JavaScript/TypeScript native backends also get W7S-managed log capture. That lets W7S expose app `console.*` output and uncaught exceptions through the [Observability](./observability.md) logs API. ## Native backend function shape A native backend is a JavaScript or TypeScript module that exposes one request handler. The module must default export either: - an object with a `fetch(request, env, ctx)` method; or - a framework app whose default export has a compatible `fetch` method. The handler receives standard runtime values: | Argument | Shape | Purpose | | --- | --- | --- | | `request` | `Request` | The incoming HTTP request. Use `new URL(request.url)` to route by pathname and query string. | | `env` | object | W7S vars, secrets, storage bindings, queues, RPC, schedules, and workflow bindings declared for the app. | | `ctx` | `ExecutionContext`-like object | Background work hooks such as `waitUntil`, when supported by the runtime. | The handler must return a `Response` or a promise that resolves to a `Response`. ```ts title="backend/index.ts" type Env = { PUBLIC_GREETING?: string; }; export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); if (url.pathname === "/api/health") { return Response.json({ok: true}); } if (url.pathname === "/api/greeting") { return Response.json({ message: env.PUBLIC_GREETING ?? "Hello from W7S", }); } return new Response("Not found", {status: 404}); }, }; ``` Use web runtime APIs instead of starting a server process. Do not call `listen()`, bind a port, or keep a Node HTTP server running inside the backend module. The W7S runtime already owns request dispatch and calls the exported `fetch` handler for each request. For fullstack apps, requests reach the backend first. If the backend returns `404` or `405` for a `GET` or `HEAD` request, W7S can fall back to the static frontend's `index.html` for SPA routes. Return explicit JSON or text responses for API routes, and return `404` or `405` for routes the backend does not own. ### Hono example Hono works well because a Hono app exposes the same `fetch` handler shape that W7S expects. ```ts title="backend/index.ts" type Bindings = { PUBLIC_GREETING?: string; }; const app = new Hono<{Bindings: Bindings}>(); app.get("/api/health", (c) => c.json({ok: true})); app.get("/api/greeting", (c) => c.json({ message: c.env.PUBLIC_GREETING ?? "Hello from Hono on W7S", }), ); app.post("/api/echo", async (c) => { const body = await c.req.json().catch(() => ({})); return c.json({body}); }); app.notFound((c) => c.text("Not found", 404)); export default app; ``` Install and bundle dependencies before the deploy action uploads the repository archive: ```yaml - run: npm ci - run: npm run build - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` If the backend uses `hono` directly from `backend/index.ts`, make sure the build step bundles it into the deployed JavaScript output or keeps the dependency available in the uploaded backend files. ## Fullstack repositories A repository can include both: ```text backend/index.ts dist/index.html ``` or a framework SSR build: ```text dist/server/index.js dist/client/assets/app.js ``` Requests go to the backend first. If the backend returns `404` or `405` for a `GET` or `HEAD` request, W7S can fall back to the static frontend's `index.html` for SPA routes. --- # Local Development Source: https://w7s.io/docs/local-development/ `w7s-local` runs a local `workerd` router that mirrors the W7S URL shape. Use it when you want to test a frontend, backend dev server, or fullstack app with the same owner, repo, branch, and path routing model that W7S uses after deploy. It does not require Wrangler, a Cloudflare account, KV, R2, D1, Durable Objects, Workers for Platforms, or a W7S deploy. ## Quick start For a backend or framework dev server: ```sh npx w7s-local@latest --backend http://localhost:5173 ``` For static build output: ```sh npx w7s-local@latest --frontend dist ``` To have `w7s-local` start the app dev server first: ```sh npx w7s-local@latest \ --command "npm run dev -- --host 127.0.0.1 --port 5173" \ --backend http://localhost:5173 ``` The local router listens on `127.0.0.1:8787` by default and prints the W7S-shaped URL when it starts. ## Local URL shape Production environment: ```text http://.local.w7s.cloud:8787// ``` Branch or named environment: ```text http://--.local.w7s.cloud:8787// ``` If local DNS for `local.w7s.cloud` is not available, send the host header yourself: ```sh curl -H "host: acme.local.w7s.cloud" http://127.0.0.1:8787/app/ ``` `w7s-local` also accepts direct `127.0.0.1` requests as a fallback, which is useful when one local repo calls another local repo on a different port. ## Common options ```text --root App root. Defaults to cwd. --owner GitHub owner/org slug. Inferred from git remote when possible. --repo GitHub repo slug. Inferred from package.json or cwd. --environment W7S environment. Defaults to production. --base-domain Local W7S base domain. Defaults to local.w7s.cloud. --port Local workerd HTTP port. Defaults to 8787. --frontend Static output directory. Auto-detected by W7S conventions. --backend Backend/dev server origin to proxy after stripping the repo prefix. --command Start a dev command before serving. --workerd workerd executable. Defaults to the bundled npm dependency. ``` Example with explicit owner, repo, environment, and port: ```sh npx w7s-local@latest \ --owner acme \ --repo app \ --environment feature-login \ --port 8788 \ --backend http://localhost:3000 ``` ## What it simulates `w7s-local` simulates: - W7S owner and environment host parsing; - repo prefix stripping before backend proxying; - static asset serving from local build output; - static exact match before backend proxy; - SPA fallback after backend `404` or `405`; - W7S request headers such as `x-w7s-org-slug`, `x-w7s-repo-slug`, and `x-w7s-original-path`. Check local router state with: ```sh curl http://127.0.0.1:8787/_w7s/local/status ``` `w7s-local` does not provision or emulate W7S-managed bindings such as storage, queues, workflows, Workers AI, or the production RPC token path. Use local service doubles or local HTTP fallbacks for those dependencies, then keep hosted smoke tests for the managed binding path. ## Test RPC between two local repos Hosted W7S RPC uses `env.W7S_RPC.fetch("https://w7s.internal/api/v1/rpc///")`. `w7s-local` does not create that internal service binding, so local multi-repo tests should put the RPC call behind a small helper: - in hosted W7S, call `env.W7S_RPC` with `env.W7S_RPC_TOKEN`; - under `w7s-local`, call the target repo's local W7S URL on another port. This tests routing, repo-prefix stripping, request serialization, response handling, and most app-level behavior locally. It does not replace a hosted smoke test for W7S RPC authorization and deployment tokens. The docs repo includes two copyable example repos: - [`examples/w7s-local-rpc-time-service`](https://github.com/w7s-io/docs/tree/main/examples/w7s-local-rpc-time-service): target service with a `/datetime` backend route. - [`examples/w7s-local-rpc-client`](https://github.com/w7s-io/docs/tree/main/examples/w7s-local-rpc-client): caller service with a `/datetime` route that calls the target. Run the target service in one terminal: ```sh cd examples/w7s-local-rpc-time-service npm install npm run local ``` Run the caller service in a second terminal: ```sh cd examples/w7s-local-rpc-client npm install npm run local ``` Call the client through its local W7S router: ```sh curl -H "host: acme.local.w7s.cloud" \ http://127.0.0.1:8789/rpc-client/datetime ``` Expected response shape: ```json { "ok": true, "source": "local-w7s-url", "target": { "ok": true, "service": "rpc-time", "received": { "routedOwner": "acme", "routedRepo": "rpc-time", "originalPath": "/rpc-time/datetime", "callerRepository": "acme/rpc-client" } } } ``` The target router runs on port `8788`; the caller router runs on port `8789`. The caller's default local target is: ```text http://127.0.0.1:8788/rpc-time/datetime ``` Override it when testing a different owner, repo, port, or path: ```sh LOCAL_RPC_DATETIME_URL=http://127.0.0.1:8788/other-service/datetime npm run local ``` ## Production RPC helper shape Keep production and local behavior in one helper: ```js title="backend/index.js" const callDatetimeService = (env) => { if (env.W7S_RPC && env.W7S_RPC_TOKEN) { return env.W7S_RPC.fetch( "https://w7s.internal/api/v1/rpc/acme/rpc-time/datetime", { headers: { authorization: `Bearer ${env.W7S_RPC_TOKEN}` } } ); } return fetch( env.LOCAL_RPC_DATETIME_URL ?? "http://127.0.0.1:8788/rpc-time/datetime", { headers: { "x-w7s-rpc": "1", "x-w7s-rpc-caller-owner": env.W7S_OWNER ?? "acme", "x-w7s-rpc-caller-repo": env.W7S_REPO ?? "rpc-client", "x-w7s-rpc-caller-repository": env.W7S_REPOSITORY ?? "acme/rpc-client", "x-w7s-rpc-caller-environment": env.W7S_ENVIRONMENT ?? "production" } } ); }; ``` The local `x-w7s-rpc-*` headers are only a development convenience so the target service can exercise caller-aware code paths. In hosted W7S, those identity headers are injected by W7S after the internal RPC authorization check. --- # Storage Bindings Source: https://w7s.io/docs/storage-bindings/ JavaScript/TypeScript native W7S backends can declare durable resources in `w7s.json`. W7S creates one set of resources per repository and environment, then reuses them on later deploys. For relational app data, the batteries-included path is `bindings.db`: a serverless DB that W7S provisions with the app. Most apps can start there without creating an external database account. See [Serverless Database](./serverless-database.md) for migrations and a complete example repo. If the app really needs an existing Postgres service, use a [Postgres binding](./backend-hyperdrive.md) instead. ```json { "bindings": { "kv": ["CACHE"], "fs": ["FILES"], "db": [ { "binding": "DB", "migrations": "migrations" } ] }, "vars": ["PUBLIC_API_KEY"], "secrets": ["PRIVATE_API_KEY"] } ``` ## Storage `bindings.kv` creates key-value namespaces: ```json { "bindings": { "kv": ["CACHE"] } } ``` `bindings.fs` creates FS buckets: ```json { "bindings": { "fs": ["FILES"] } } ``` `bindings.db` creates serverless DBs. The full setup flow is documented in [Serverless Database](./serverless-database.md). ```json { "bindings": { "db": [{ "binding": "DB" }] } } ``` By default, W7S generates resource names from the environment, owner, repo, resource type, and binding name. You can provide explicit names: ```json { "bindings": { "kv": [{ "binding": "CACHE", "name": "my-cache" }], "fs": [{ "binding": "FILES", "name": "my-files" }], "db": [{ "binding": "DB", "name": "my-db" }] } } ``` ## SQL migrations Point a SQL binding at a migrations directory: ```json { "bindings": { "db": [{ "binding": "DB", "migrations": "migrations" }] } } ``` W7S applies `.sql` files in sorted order. Applied migration filenames are tracked in `_w7s_migrations` inside the app database. See [Serverless Database](./serverless-database.md#add-migrations) for migration file examples. ## Runtime Values Names listed in `vars` and `secrets` are collected automatically by the official GitHub Action when those values are present in the workflow environment: ```yaml - uses: w7s-io/w7s-cloud@v1 env: PUBLIC_API_KEY: ${{ vars.PUBLIC_API_KEY }} PRIVATE_API_KEY: ${{ secrets.PRIVATE_API_KEY }} with: token: ${{ github.token }} ``` You can also pass names directly: ```yaml - uses: w7s-io/w7s-cloud@v1 env: PUBLIC_API_KEY: ${{ vars.PUBLIC_API_KEY }} with: token: ${{ github.token }} vars: PUBLIC_API_KEY ``` Secret values are passed as backend secret bindings. Deploy summaries show secret counts, not secret values. W7S also injects platform bindings for JavaScript/TypeScript native backends, including `W7S_RPC` for backend-to-backend calls, `W7S_QUEUE` for background work, and `W7S_AI` for model calls. See [Backend RPC](./backend-rpc.md), [Backend Queues](./backend-queues.md), and [Backend AI](./backend-ai.md). --- # Serverless Database Source: https://w7s.io/docs/serverless-database/ W7S can provision a serverless DB for a JavaScript/TypeScript native backend. Declare the database in `w7s.json`, keep SQL migrations in the repo, and read the binding from `env.DB` inside the backend worker. The full Drizzle example is available here: - GitHub: [w7s-io/example-serverless-database](https://github.com/w7s-io/example-serverless-database) - Live app: [w7s-io.w7s.cloud/example-serverless-database](https://w7s-io.w7s.cloud/example-serverless-database/) - Live notes API: [w7s-io.w7s.cloud/example-serverless-database/api/notes](https://w7s-io.w7s.cloud/example-serverless-database/api/notes) ## Declare The Database Add a `db` binding in `w7s.json`. The binding name becomes the property available on `env`. ```json { "bindings": { "db": [ { "binding": "DB", "migrations": "migrations" } ] } } ``` W7S creates and reuses one database per repository and environment. For the manifest above, backend code reads the database at `env.DB`. ## Add Migrations Keep migrations as plain `.sql` files in the directory named by `migrations`. ```text migrations/ 0001_create_notes.sql 0002_seed_notes.sql ``` ```sql title="migrations/0001_create_notes.sql" CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, body TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_notes_created_at ON notes (created_at); ``` ```sql title="migrations/0002_seed_notes.sql" INSERT INTO notes (body) VALUES ('Hello from the W7S serverless database'); ``` W7S applies `.sql` files in sorted order during deploy. Applied migration filenames are tracked in `_w7s_migrations` inside the app database, so later deploys only run new migrations. ## Use Drizzle Install Drizzle and a normal TypeScript build toolchain: ```sh npm install drizzle-orm npm install -D drizzle-kit esbuild typescript @cloudflare/workers-types ``` The Drizzle example uses Cloudflare D1 types and the D1 adapter because the W7S DB runtime is based on Cloudflare D1 today and exposes the same database features. The public W7S manifest still uses `db` and `DB`, so apps are written against the W7S DB abstraction rather than the backing provider name. Define the schema once and let Drizzle type your queries: ```ts title="backend/src/schema.ts" export const notes = sqliteTable("notes", { id: integer("id").primaryKey({ autoIncrement: true }), body: text("body").notNull(), createdAt: text("created_at").notNull().default(sql`CURRENT_TIMESTAMP`) }); ``` Point Drizzle Kit at the schema if you want to generate future SQL migrations from the TypeScript schema: ```ts title="drizzle.config.ts" export default defineConfig({ schema: "./backend/src/schema.ts", out: "./migrations", dialect: "sqlite" }); ``` ## Query From The Backend Native backends default-export a `fetch(request, env, ctx)` handler. The database binding is available on `env.DB`. ```ts title="backend/src/index.ts" type Env = { DB: D1Database; }; export default { async fetch(request: Request, env: Env): Promise { const url = new URL(request.url); const db = drizzle(env.DB); if (url.pathname === "/api/notes" && request.method === "GET") { const rows = await db .select() .from(notes) .orderBy(desc(notes.createdAt)) .limit(20); return Response.json({ notes: rows }); } if (url.pathname === "/api/notes" && request.method === "POST") { const { body } = await request.json<{ body: string }>(); const [note] = await db .insert(notes) .values({ body, createdAt: new Date().toISOString() }) .returning(); return Response.json({ note }, { status: 201 }); } return Response.json({ status: "ok" }); } }; ``` The example repo includes a complete notes API with input validation, a health endpoint, and a GitHub Actions smoke test that writes and reads a note after deploy. ## Deploy It Bundle npm dependencies into `backend/index.js` before running the W7S action. W7S native backends can use npm packages, but the deployed backend entrypoint should not depend on bare npm imports at runtime. ```json title="package.json" { "scripts": { "build": "esbuild backend/src/index.ts --bundle --format=esm --platform=neutral --target=es2022 --minify --legal-comments=none --outfile=backend/index.js", "check": "tsc --noEmit && npm run build" } } ``` Then run the check before deploy: ```yaml name: Deploy on: push: branches: - main workflow_dispatch: permissions: contents: read jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm run check - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` ## When To Use It Use the W7S serverless database for app-local relational data: users, settings, notes, small product catalogs, internal dashboards, prototypes, and early production apps that benefit from SQL without separate database setup. If the app already depends on an external Postgres service, use [Postgres Bindings](./backend-hyperdrive.md) instead. --- # Stateful Objects Source: https://w7s.io/docs/backend-durable-objects/ JavaScript/TypeScript native W7S backends can declare stateful object classes in `w7s.json`. W7S creates persistent classes the first time it sees them and binds each class to the backend by name. Working example repository: - [`w7s-io/example-durable-counter`](https://github.com/w7s-io/example-durable-counter): backend that uses a stateful object as a persistent counter. ## Declare a stateful object Add `bindings.durableObjects` to `w7s.json`: ```json title="w7s.json" { "bindings": { "durableObjects": [ { "binding": "COUNTER", "className": "Counter" } ] } } ``` The backend must export the class named by `className`. ## Implement the class ```js title="backend/index.js" export class Counter { constructor(ctx) { this.ctx = ctx; } async fetch(request) { const count = (await this.ctx.storage.get("count")) ?? 0; if (new URL(request.url).pathname === "/increment") { await this.ctx.storage.put("count", count + 1); return Response.json({ count: count + 1 }); } return Response.json({ count }); } } export default { fetch(request, env) { const id = env.COUNTER.idFromName("global"); return env.COUNTER.get(id).fetch(request); } }; ``` ## Redeploy behavior Stateful object apps use a stable runtime identity for each GitHub owner, repo, and environment. This is different from regular JavaScript/TypeScript native backends, which use commit-specific runtime identities. The stable identity is what lets state survive W7S redeploys. W7S automatically creates newly declared persistent classes. It does not automate class renames, transfers, or deletes yet. ## Live example - App: [`w7s-io.w7s.cloud/example-durable-counter`](https://w7s-io.w7s.cloud/example-durable-counter/) - Current value: [`/value`](https://w7s-io.w7s.cloud/example-durable-counter/value) The example workflow reads the current counter, increments it twice, and verifies the new value after deploy. That proves the stateful binding works and state persists across W7S redeploys. --- # Postgres Bindings Source: https://w7s.io/docs/backend-hyperdrive/ JavaScript/TypeScript native W7S backends can declare managed Postgres bindings in `w7s.json`. These bindings let backend modules connect to external Postgres databases through the W7S-managed connection layer. W7S currently binds existing managed Postgres configs by ID. It does not create those configs or rotate database credentials yet. Working example repository: - [`w7s-io/example-postgres-binding`](https://github.com/w7s-io/example-postgres-binding): backend source, build workflow, runtime compatibility metadata, and manual deploy workflow for a managed Postgres binding. ## Create a Postgres binding config Create or request the managed Postgres binding config from the W7S operator, then copy its ID: ```text postgres://user:password@host:5432/database ``` ## Declare the binding Add `bindings.hyperdrive` to `w7s.json`: ```json title="w7s.json" { "bindings": { "hyperdrive": [ { "binding": "DB", "id": "postgres-binding-id" } ] } } ``` W7S exposes this binding to the backend at `env.DB`. ## Use it from a backend Most Postgres clients need to be bundled in CI before deployment. A typical backend uses the managed connection string: ```ts title="backend/index.ts" type Env = { DB: { connectionString: string; }; }; export default { async fetch(_request: Request, env: Env) { const sql = postgres(env.DB.connectionString); const rows = await sql`select now() as now`; return Response.json({ now: rows[0]?.now }); } }; ``` Because W7S does not install dependencies or bundle apps during deploy, run your build in GitHub Actions before `w7s-io/w7s-cloud@v1`. ## Compatibility flags Many Postgres drivers require Node.js compatibility in edge runtimes. If your build emits `dist/server/index.js`, include runtime compatibility metadata with the backend output: ```json title="runtime compatibility metadata" { "compatibility_flags": ["nodejs_compat_v2"] } ``` W7S reads supported compatibility flags from the backend output when uploading the backend module. ## Limitations - Managed Postgres bindings require a JavaScript or TypeScript native backend deployment. - W7S does not create or update managed Postgres configs yet. - W7S does not manage origin database credentials. - The shared example is manual-deploy because a useful live smoke test requires a real external database and managed Postgres binding config ID. --- # Backend AI Source: https://w7s.io/docs/backend-ai/ JavaScript/TypeScript native W7S backends can use W7S AI without adding provider credentials to the repo. W7S injects these bindings during deploy: - `W7S_AI`: internal service binding to the W7S AI runner. - `W7S_AI_TOKEN`: W7S-generated secret token for this deployment. No `w7s.json` entry, W7S account, provider account, provider API token, card, or GitHub secret is required for hosted deploys that use this binding. ## Run a Model ```ts title="backend/index.ts" type Env = { W7S_AI: Fetcher; W7S_AI_TOKEN: string; }; export default { async fetch(_request: Request, env: Env) { const response = await env.W7S_AI.fetch("https://w7s.internal/api/v1/ai/run", { method: "POST", headers: { authorization: `Bearer ${env.W7S_AI_TOKEN}`, "content-type": "application/json" }, body: JSON.stringify({ model: "@w7s/meta/llama-3.1-8b-instruct-fp8", input: { prompt: "Write one short, original deployment joke." } }) }); return response; } }; ``` The response is JSON: ```json { "status": "success", "data": { "model": "@w7s/meta/llama-3.1-8b-instruct-fp8", "result": { "response": "..." } } } ``` ## Limits W7S resolves the caller from the deployment token, checks app suspension, applies `ai.run` usage limits, and then calls the platform-owned AI binding. The first supported model is `@w7s/meta/llama-3.1-8b-instruct-fp8`; more models can be enabled by the W7S platform operator. See [`w7s-io/example-ai-joke`](https://github.com/w7s-io/example-ai-joke) for a full frontend plus backend example. --- # Backend RPC Source: https://w7s.io/docs/backend-rpc/ JavaScript/TypeScript native W7S backends can call other W7S backends without going through public DNS. W7S injects an internal service binding and a per-deployment token into every JavaScript/TypeScript native backend. Working example repositories: - [`w7s-io/example-rpc-datetime`](https://github.com/w7s-io/example-rpc-datetime): target backend that returns the current UTC datetime. - [`w7s-io/example-rpc-client`](https://github.com/w7s-io/example-rpc-client): public backend that calls the datetime service through RPC. For local two-repo testing before deploy, see [Local Development](./local-development.md#test-rpc-between-two-local-repos). ## Runtime bindings Every JavaScript/TypeScript native backend receives: ```text W7S_RPC W7S_RPC_TOKEN W7S_OWNER W7S_REPO W7S_REPOSITORY W7S_ENVIRONMENT ``` `W7S_RPC` is an internal service binding to the W7S control plane. `W7S_RPC_TOKEN` is a secret used by W7S to prove which deployed app is making the call. Only send the bearer token. W7S resolves the caller repo and deployment environment from that token. ## Call another backend Call the target through: ```text /api/v1/rpc/// ``` Example: ```ts const response = await env.W7S_RPC.fetch( "https://w7s.internal/api/v1/rpc/guerrerocarlos/auth/session", { headers: { authorization: `Bearer ${env.W7S_RPC_TOKEN}`, cookie: request.headers.get("cookie") ?? "" } } ); if (!response.ok) { return new Response("Auth service unavailable", { status: 502 }); } const session = await response.json(); ``` The target backend receives the request at `/session` in this example. ## Example: same-owner apps Assume these two repositories exist under the same GitHub owner: ```text github.com/guerrerocarlos/auth github.com/guerrerocarlos/nodepad ``` `guerrerocarlos/nodepad` can call `guerrerocarlos/auth` without extra configuration because both apps share the same owner. ### Target backend In `guerrerocarlos/auth`, expose a normal backend route: ```ts title="backend/index.ts" export default { async fetch(request: Request) { const url = new URL(request.url); if (url.pathname === "/session") { return Response.json({ ok: true, user: { id: "user_123", email: "hello@example.com" }, calledBy: request.headers.get("x-w7s-rpc-caller-repository") }); } return new Response("Not found", { status: 404 }); } }; ``` Deploying this repo makes the route available internally as: ```text /api/v1/rpc/guerrerocarlos/auth/session ``` ### Caller backend In `guerrerocarlos/nodepad`, call the auth backend through `env.W7S_RPC`: ```ts title="backend/index.ts" type Env = { W7S_RPC: Fetcher; W7S_RPC_TOKEN: string; }; export default { async fetch(_request: Request, env: Env) { const response = await env.W7S_RPC.fetch( "https://w7s.internal/api/v1/rpc/guerrerocarlos/auth/session", { headers: { authorization: `Bearer ${env.W7S_RPC_TOKEN}` } } ); if (!response.ok) { return new Response("Auth service failed", { status: 502 }); } const session = await response.json(); return Response.json({ session }); } }; ``` ## Example: reusable helper For apps that call multiple W7S services, keep the request headers in one helper: ```ts title="backend/w7s-rpc.ts" type W7sRpcEnv = { W7S_RPC: Fetcher; W7S_RPC_TOKEN: string; }; export const callW7s = ( env: W7sRpcEnv, target: `${string}/${string}`, path: `/${string}`, init: RequestInit = {} ) => { const headers = new Headers(init.headers); headers.set("authorization", `Bearer ${env.W7S_RPC_TOKEN}`); return env.W7S_RPC.fetch( `https://w7s.internal/api/v1/rpc/${target}${path}`, { ...init, headers } ); }; ``` Use it from a backend: ```ts title="backend/index.ts" export default { async fetch(request: Request, env: Env) { const response = await callW7s(env, "guerrerocarlos/auth", "/session", { headers: { cookie: request.headers.get("cookie") ?? "" } }); return response.ok ? response : new Response("Auth service failed", { status: 502 }); } }; ``` ## Authorization Apps under the same GitHub owner can call each other by default. For cross-owner calls, the target app must allow the caller in `w7s.json`. To allow one exact repo: ```json { "rpc": { "allow": ["guerrerocarlos/nodepad"] } } ``` To allow every repo under an owner: ```json { "rpc": { "allow": ["guerrerocarlos"] } } ``` Allowlist entries can be: - a GitHub owner, such as `w7s-io`; - an exact GitHub repository, such as `guerrerocarlos/nodepad`. The target deployment controls this list. The caller cannot grant itself access. ## Target identity headers W7S injects caller identity headers before dispatching to the target backend: ```text x-w7s-rpc: 1 x-w7s-rpc-caller-owner: x-w7s-rpc-caller-repo: x-w7s-rpc-caller-repository: / x-w7s-rpc-caller-environment: ``` Use these headers for app-level authorization, audit logs, or tenant routing. ## Environment behavior RPC calls stay in the caller's deployment environment. If a feature branch deploy calls `owner/service`, W7S looks for the target deployment in that same branch environment. Production callers use production targets. --- # Backend Queues Source: https://w7s.io/docs/backend-queues/ JavaScript/TypeScript native W7S backends can declare queues in `w7s.json`. W7S creates the managed queue, connects it to the W7S control plane, and delivers batches to the declaring backend. Working example repositories: - [`w7s-io/example-queue-worker`](https://github.com/w7s-io/example-queue-worker): backend that enqueues work to its own queue and stores the latest processed message. - [`w7s-io/example-queue-consumer`](https://github.com/w7s-io/example-queue-consumer): backend that owns and consumes a queue. - [`w7s-io/example-queue-producer`](https://github.com/w7s-io/example-queue-producer): separate backend that sends messages to the consumer's queue. ## Declare a queue The simplest form declares a queue named `jobs`: ```json title="w7s.json" { "queues": ["jobs"] } ``` By default, W7S delivers `jobs` batches to: ```text /_w7s/queues/jobs ``` Use an object when you want a different consumer path: ```json title="w7s.json" { "queues": [ { "name": "emails", "consumer": "/internal/queues/emails" } ] } ``` Queues require a JavaScript or TypeScript native backend deployment. Static-only apps cannot declare queues. ## Runtime bindings Every JavaScript/TypeScript native backend receives: ```text W7S_QUEUE W7S_QUEUE_TOKEN W7S_OWNER W7S_REPO W7S_REPOSITORY W7S_ENVIRONMENT ``` `W7S_QUEUE` is an internal service binding to the W7S control plane. `W7S_QUEUE_TOKEN` is a secret used by W7S to prove which deployed app is enqueueing the message. Only send the bearer token. W7S resolves the caller repo and deployment environment from that token. ## Send a message Send messages through: ```text /api/v1/queues/// ``` Example: ```ts title="backend/index.ts" type Env = { W7S_QUEUE: Fetcher; W7S_QUEUE_TOKEN: string; }; export default { async fetch(_request: Request, env: Env) { const response = await env.W7S_QUEUE.fetch( "https://w7s.internal/api/v1/queues/w7s-io/example-queue-worker/jobs", { method: "POST", headers: { authorization: `Bearer ${env.W7S_QUEUE_TOKEN}`, "content-type": "application/json" }, body: JSON.stringify({ type: "ping", createdAt: new Date().toISOString() }) } ); return response.ok ? Response.json({ status: "queued" }) : new Response(await response.text(), { status: 502 }); } }; ``` To delay delivery, add: ```text x-w7s-queue-delay-seconds: 60 ``` The delay value must be an integer from `0` to `86400`. W7S rejects queue JSON envelopes larger than 64 KB by default. Queue sends also count against daily and short-window limits for the caller repo, owner, and global platform. ## Receive messages Create a backend route for the queue consumer path: ```ts title="backend/index.ts" export default { async fetch(request: Request) { const url = new URL(request.url); if (url.pathname === "/_w7s/queues/jobs" && request.method === "POST") { const batch = await request.json(); for (const message of batch.messages) { console.log("received", message.id, message.body); } return Response.json({ ok: true, processed: batch.messages.length }); } return new Response("Not found", { status: 404 }); } }; ``` W7S sends this payload to the consumer route: ```json { "queue": "jobs", "queueName": "w7s-production-w7s-io-example-queue-worker-queue-jobs", "messages": [ { "id": "message-id", "attempts": 1, "timestamp": "2026-05-24T22:00:00.000Z", "enqueuedAt": "2026-05-24T21:59:00.000Z", "caller": { "orgSlug": "w7s-io", "repoSlug": "example-queue-worker", "repository": "w7s-io/example-queue-worker", "environment": "production" }, "body": { "type": "ping" } } ] } ``` Return any `2xx` response after processing. Non-`2xx` responses make W7S report the queue delivery as failed so the managed queue can retry the batch. W7S-created queue consumers use bounded defaults: batch size 10, max retries 3, and retry delay 10 seconds. ## Separate producer and consumer A backend does not need to own a queue in order to produce messages. It only needs to be a JavaScript/TypeScript native W7S backend, because W7S injects `W7S_QUEUE` and `W7S_QUEUE_TOKEN` into every JavaScript/TypeScript native backend. The consumer declares and receives the queue: ```json title="w7s.json" { "bindings": { "kv": ["STATE"] }, "queues": ["jobs"] } ``` The producer sends to that consumer's queue: ```ts title="backend/index.ts" await env.W7S_QUEUE.fetch( "https://w7s.internal/api/v1/queues/w7s-io/example-queue-consumer/jobs", { method: "POST", headers: { authorization: `Bearer ${env.W7S_QUEUE_TOKEN}`, "content-type": "application/json" }, body: JSON.stringify({ id: crypto.randomUUID(), text: "work for the consumer" }) } ); ``` The live pair is: - Producer: [`w7s-io/example-queue-producer`](https://github.com/w7s-io/example-queue-producer), served at [`w7s-io.w7s.cloud/example-queue-producer`](https://w7s-io.w7s.cloud/example-queue-producer/) - Consumer: [`w7s-io/example-queue-consumer`](https://github.com/w7s-io/example-queue-consumer), served at [`w7s-io.w7s.cloud/example-queue-consumer`](https://w7s-io.w7s.cloud/example-queue-consumer/) Try the producer: ```sh curl -X POST "https://w7s-io.w7s.cloud/example-queue-producer/enqueue?text=hello" ``` The response includes a `checkDeliveryAt` URL. Queue delivery is asynchronous, so poll that URL until the consumer has processed the message. ## Authorization Apps under the same GitHub owner can enqueue messages to each other by default. For cross-owner sends, the target app must allow the caller in `w7s.json`. To allow one exact repo: ```json { "queue": { "allow": ["guerrerocarlos/nodepad"] } } ``` To allow every repo under an owner: ```json { "queue": { "allow": ["guerrerocarlos"] } } ``` Allowlist entries can be: - a GitHub owner, such as `w7s-io`; - an exact GitHub repository, such as `guerrerocarlos/nodepad`. The target deployment controls this list. The caller cannot grant itself access. ## Environment behavior Queue sends stay in the caller's deployment environment. If a feature branch deploy sends to `owner/service`, W7S looks for the target deployment in that same branch environment. Production callers use production targets. --- # Backend Workflows Source: https://w7s.io/docs/backend-workflows/ JavaScript/TypeScript native W7S backends can declare workflow consumers in `w7s.json`. W7S owns the workflow runner, creates instances for app requests, and dispatches each instance to the declaring backend through a durable step with retries. ## Declare a workflow The simplest form declares a workflow named `process-order`: ```json title="w7s.json" { "workflows": ["process-order"] } ``` By default, W7S delivers that workflow to: ```text /_w7s/workflows/process-order ``` Use an object when you want a different consumer path: ```json title="w7s.json" { "workflows": [ { "name": "process-order", "path": "/internal/workflows/process-order" } ] } ``` Workflows require a JavaScript or TypeScript native backend deployment. Static-only apps cannot declare workflows. ## Runtime bindings Every JavaScript/TypeScript native backend receives: ```text W7S_WORKFLOW W7S_WORKFLOW_TOKEN W7S_OWNER W7S_REPO W7S_REPOSITORY W7S_ENVIRONMENT ``` `W7S_WORKFLOW` is an internal service binding to the W7S control plane. `W7S_WORKFLOW_TOKEN` is a secret used by W7S to prove which deployed app is starting the workflow. Only send the bearer token. W7S resolves the caller repo and deployment environment from that token. ## Start a workflow Start workflow instances through: ```text /api/v1/workflows/// ``` Example: ```ts title="backend/index.ts" type Env = { W7S_WORKFLOW: Fetcher; W7S_WORKFLOW_TOKEN: string; }; export default { async fetch(_request: Request, env: Env) { const response = await env.W7S_WORKFLOW.fetch( "https://w7s.internal/api/v1/workflows/w7s-io/example-workflows/process-order", { method: "POST", headers: { authorization: `Bearer ${env.W7S_WORKFLOW_TOKEN}`, "content-type": "application/json", "x-w7s-workflow-instance-id": crypto.randomUUID() }, body: JSON.stringify({ orderId: "123" }) } ); return response.ok ? Response.json({ status: "started", result: await response.json() }) : new Response(await response.text(), { status: 502 }); } }; ``` `x-w7s-workflow-instance-id` is optional. If omitted, W7S generates an id. W7S rejects workflow instance payloads larger than 64 KB by default. Starts also count against daily and short-window limits for the caller repo, owner, and global platform. Target repos are capped at 50 active workflow instances by default. ## Receive workflow runs Create a backend route for the workflow consumer path: ```ts title="backend/index.ts" export default { async fetch(request: Request) { const url = new URL(request.url); if (url.pathname === "/_w7s/workflows/process-order" && request.method === "POST") { const run = await request.json(); console.log("workflow", run.instanceId, run.payload); return Response.json({ ok: true, processedOrderId: run.payload.orderId }); } return new Response("Not found", { status: 404 }); } }; ``` W7S sends this payload to the consumer route: ```json { "workflow": "process-order", "workflowName": "process-order", "instanceId": "production-w7s-io-example-workflows-process-order-abc123", "createdAt": "2026-05-26T00:00:00.000Z", "caller": { "orgSlug": "w7s-io", "repoSlug": "example-workflows", "repository": "w7s-io/example-workflows", "environment": "production" }, "target": { "orgSlug": "w7s-io", "repoSlug": "example-workflows", "repository": "w7s-io/example-workflows", "environment": "production", "workflow": "process-order", "path": "/_w7s/workflows/process-order" }, "payload": { "orderId": "123" } } ``` Return any `2xx` response after processing. Non-`2xx` responses make the workflow step fail and retry according to W7S core retry policy. The default delivery policy is 3 retries, 10 second initial delay, exponential backoff, and 300 second timeout. ## Status Check an instance through: ```text GET /api/v1/workflows//// ``` Use the same `Authorization: Bearer ${env.W7S_WORKFLOW_TOKEN}` header used to start the workflow. ## Authorization Apps under the same GitHub owner can start each other's workflows by default. For cross-owner starts, the target app must allow the caller in `w7s.json`. To allow one exact repo: ```json title="w7s.json" { "workflow": { "allow": ["guerrerocarlos/nodepad"] } } ``` To allow every repo under an owner: ```json title="w7s.json" { "workflow": { "allow": ["guerrerocarlos"] } } ``` ## Current model W7S apps do not define platform-specific workflow classes directly. W7S starts a managed workflow instance and dispatches one durable step to the app's HTTP consumer path. This keeps the app API simple and lets W7S keep the underlying workflow implementation transparent to the app. --- # Backend Schedules Source: https://w7s.io/docs/backend-schedules/ JavaScript/TypeScript native W7S backends can declare cron schedules in `w7s.json`. W7S runs one managed scheduler every minute, checks deployed app schedules, and dispatches due jobs to the declaring backend. Working example repository: - [`w7s-io/example-schedules`](https://github.com/w7s-io/example-schedules): backend that receives a one-minute schedule and stores the latest tick in key-value storage. ## Declare a schedule Add a `schedules` array to `w7s.json`: ```json title="w7s.json" { "schedules": [ { "cron": "*/5 * * * *", "path": "/_w7s/schedules/sync" } ] } ``` Schedules require a JavaScript or TypeScript native backend deployment. Static-only apps cannot declare schedules. ## Cron format W7S supports five-field UTC cron expressions: ```text minute hour day-of-month month day-of-week ``` Supported field syntax: - `*` - `*/n` - numeric values - comma-separated lists - ranges such as `9-17` - stepped ranges such as `9-17/2` Day-of-week accepts `0` or `7` for Sunday. W7S matches all five fields against the scheduled UTC minute. ## Receive a scheduled job Implement the route from the schedule declaration: ```ts title="backend/index.ts" export default { async fetch(request: Request) { const url = new URL(request.url); if (request.method === "POST" && url.pathname === "/_w7s/schedules/sync") { const job = await request.json(); console.log("scheduled job", job.schedule, job.scheduledTime); return Response.json({ ok: true }); } return new Response("Not found", { status: 404 }); } }; ``` W7S sends this JSON payload: ```json { "schedule": "*/5 * * * *", "scheduledTime": "2026-05-25T12:00:00.000Z", "repository": "owner/repo", "environment": "production" } ``` The backend also receives schedule identity headers: ```text x-w7s-schedule: 1 x-w7s-schedule-cron: */5 * * * * x-w7s-schedule-time: 2026-05-25T12:00:00.000Z ``` Return any `2xx` response after processing. Non-`2xx` responses make W7S report the schedule dispatch as failed. ## With storage Schedules work well with per-app key-value, FS, or SQL bindings. For example, a scheduled backend can keep the latest run in key-value storage: ```json title="w7s.json" { "bindings": { "kv": ["STATE"] }, "schedules": [ { "cron": "* * * * *", "path": "/_w7s/schedules/tick" } ] } ``` ```ts title="backend/index.ts" type Env = { STATE: { put(key: string, value: string): Promise; get(key: string, type: "json"): Promise; }; }; export default { async fetch(request: Request, env: Env) { const url = new URL(request.url); if (request.method === "POST" && url.pathname === "/_w7s/schedules/tick") { const job = await request.json(); await env.STATE.put("latest-schedule", JSON.stringify(job)); return Response.json({ ok: true }); } if (url.pathname === "/last") { const latest = await env.STATE.get("latest-schedule", "json"); return Response.json({ latest }); } return new Response("Not found", { status: 404 }); } }; ``` ## Environment behavior Schedules are scoped to the deployment environment. A production deploy and a branch deploy can declare different schedules without sharing the same mapping. W7S stores schedule mappings in its deployment state and removes stale mappings when the app is redeployed. The live example is served at [`w7s-io.w7s.cloud/example-schedules`](https://w7s-io.w7s.cloud/example-schedules/). Check the latest delivered tick at [`/last`](https://w7s-io.w7s.cloud/example-schedules/last). --- # Usage Accounting Source: https://w7s.io/docs/usage-accounting/ W7S records daily usage rollups for each deployed repository and environment. W7S-managed paths update counters directly, and direct runtime and storage usage is synced hourly from platform telemetry into the same rollup. The usage response also includes effective daily limits and warnings. W7S enforces immediate limits on deploys, runtime requests, RPC dispatches, queue sends, workflow starts, log ingestion, and internal queue/schedule/workflow deliveries. Direct binding usage such as SQL, FS, key-value, and stateful object cost is enforced after the hourly platform sync. Repo events are also mirrored into owner-level and global aggregate rollups. Runtime guards check repo, owner, and global scopes so a single owner cannot multiply the free tier across many repos, and the shared platform has a final circuit breaker. ## Read usage Use the usage API with a GitHub token that can access the target repo: ```sh curl "https://w7s.cloud/api/v1/usage//?date=2026-05-26" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` Include hourly platform records: ```sh curl "https://w7s.cloud/api/v1/usage//?date=2026-05-26&include=hourly" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` By default, usage reads the `production` environment. Override the environment with either: ```text ?environment=staging x-w7s-environment: staging ``` Environment values are normalized the same way as deploy environments, so branch names and overrides resolve to DNS-safe names such as `feature-api-v2-test`. Read the effective limit policy without usage counters: ```sh curl "https://w7s.cloud/api/v1/limits//" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` The bearer token must be able to access the same GitHub repository. ## Response ```json { "status": "success", "data": { "usage": { "version": 1, "date": "2026-05-26", "orgSlug": "w7s-io", "repoSlug": "example-workflows", "environment": "production", "repository": "w7s-io/example-workflows", "metrics": { "workflow.create": { "count": 4, "units": 4, "success": 4, "error": 0, "lastAt": "2026-05-26T12:00:00.000Z" } }, "platformSyncedAt": "2026-05-26T13:03:00.000Z", "platformHours": ["2026-05-26T12"], "updatedAt": "2026-05-26T12:00:00.000Z" }, "limits": { "version": 1, "period": "daily", "mode": "enforce", "metrics": { "workflow.create": { "metric": "workflow.create", "used": 4, "limit": 1000, "remaining": 996, "usageRatio": 0.004, "status": "ok", "source": "default" } }, "warnings": [] }, "policy": { "version": 1, "period": "daily", "mode": "enforce", "environment": "production", "orgSlug": "w7s-io", "repoSlug": "example-workflows", "policy": { "workflow.create": { "metric": "workflow.create", "dailyUnits": 1000, "warningThreshold": 0.8, "source": "default" } }, "lookups": [] }, "warnings": [] } } ``` An app with no usage for the requested day returns the same shape with an empty usage `metrics` object and `updatedAt: null`. ## Metrics Current metric IDs: ```text deploy runtime.request worker.request runtime.cpu_ms worker.script static.fs_class_a static.fs_class_b fs.class_a fs.class_b fs.storage_bytes kv.read kv.write kv.delete kv.list kv.storage_bytes db.rows_read db.rows_written db.read_queries db.write_queries db.storage_bytes durable_object.request durable_object.duration_ms durable_object.rows_read durable_object.rows_written durable_object.storage_read_units durable_object.storage_write_units durable_object.storage_deletes rpc.dispatch queue.send queue.delivery schedule.delivery workflow.create workflow.delivery ai.run log.write ``` `count` is the event count. `units` is usually the same value. Batch-like paths can record more units than a single event, such as queue delivery batches, bytes, rows, or CPU milliseconds. Platform-synced metrics can use `source: "platform"` or `source: "platform_estimated"`. ## Daily limits Current default daily limits: ```text deploy 50 runtime.request 10000 worker.request 10000 runtime.cpu_ms 300000 worker.script 5 static.fs_class_a 1000 static.fs_class_b 20000 fs.class_a 1000 fs.class_b 20000 fs.storage_bytes 104857600 kv.read 10000 kv.write 1000 kv.delete 1000 kv.list 1000 kv.storage_bytes 52428800 db.rows_read 100000 db.rows_written 10000 db.read_queries 10000 db.write_queries 1000 db.storage_bytes 52428800 durable_object.request 5000 durable_object.duration_ms 300000 durable_object.rows_read 100000 durable_object.rows_written 10000 durable_object.storage_read_units 100000 durable_object.storage_write_units 10000 durable_object.storage_deletes 10000 rpc.dispatch 10000 queue.send 10000 queue.delivery 10000 schedule.delivery 2000 workflow.create 1000 workflow.delivery 1000 ai.run 100 log.write 5000 ``` Owner-level default limits are 10x the repo defaults, with minimums of 200 deploys/day and 50 runtime scripts/day. Global default limits are 100x the repo defaults, with minimums of 2,000 deploys/day and 1,000 runtime scripts/day. Each metric gets one of these statuses: ```text ok below 80% warning at or above 80% exceeded above 100% ``` Non-`ok` metrics are also listed in `warnings` for simpler dashboards and CLI output. Requests that would push a metric above its effective daily limit return HTTP `429`. The `w7s-io/w7s-cloud@v1` GitHub Action reads this API after a successful deploy. When warnings exist, it adds them to the GitHub Actions summary and opens or updates one GitHub issue for that repo/environment. Issue notifications require this workflow permission: ```yaml permissions: contents: read issues: write ``` Set `usage-warnings-issue: false` on the action to keep warnings in the workflow summary only. Daily quota checks can run the action in usage-check-only mode from a separate workflow: ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} usage-check-only: true ``` In this mode the action does not package or deploy the repository. It only reads the current day's W7S usage and creates or updates the usage warning issue when warnings exist. See [Recommendations](./recommendations.md) for the complete daily workflow. ## Enforcement W7S has an internal `checkUsageLimit(...)` helper for expensive primitives. It reads current usage, applies the effective policy, and reports whether projected usage would exceed the daily limit. The hook now reports hard enforcement: ```json { "mode": "enforce", "enforcement": "hard", "metric": "workflow.create", "scope": "repo", "used": 8, "requestedUnits": 3, "projectedUnits": 11, "limit": 10, "status": "warning", "projectedStatus": "exceeded", "wouldBlock": true } ``` When `wouldBlock` is true, public APIs return HTTP `429`. Internal queue, schedule, and workflow delivery paths skip dispatch once their delivery metric would exceed the effective daily limit. W7S also applies short-window burst limits: ```text deploy repo 5/hour owner 25/hour global 200/hour runtime.request repo 300/min owner 2000/min global 10000/min rpc.dispatch repo 120/min owner 600/min global 5000/min queue.send repo 120/min owner 600/min global 5000/min queue.delivery repo 300/min owner 1500/min global 10000/min schedule.delivery repo 30/min owner 200/min global 2000/min workflow.create repo 60/min owner 300/min global 2000/min workflow.delivery repo 120/min owner 600/min global 5000/min ai.run repo 30/min owner 150/min global 1000/min log.write repo 500/min owner 2000/min global 10000/min ``` ## Hourly platform sync The W7S core cron runs once per minute for app schedules and also takes an hourly lock named `usage_collect_lock:v1:`. The collector queries platform telemetry for the previous closed hour, stores records under: ```text usage_platform_hourly:v1:::: ``` Then it merges those hourly records into the daily rollup. If any reliably attributed metric exceeds its effective daily limit, W7S stores: ```text app_limit_state:v1::: ``` Suspended apps return HTTP `429` before static serving, backend dispatch, deploys, RPC, queue sends, or workflow starts. Apps automatically resume at the next UTC day unless an operator writes a stricter state. Direct binding limits are delayed by the hourly sync. Immediate protection comes from deploy shape caps, runtime request limits, short-window burst limits, and runtime CPU limits on native backends. Static asset storage is capped by deploy shape limits, and immutable static assets are served through a platform cache using versioned asset keys to reduce FS reads. Stateful object storage operation units are attributed by namespace ID when W7S can discover namespace IDs from invocation telemetry; stored bytes are not per-app attributable in the current platform telemetry schema and remain a tracked gap. Queue sends reject JSON envelopes larger than 64 KB by default. New Queue consumers use bounded batch and retry settings: batch size 10, max retries 3, and retry delay 10 seconds. Workflow starts reject instance payloads larger than 64 KB by default. W7S also tracks active workflow instances and blocks new starts for a target repo at 50 active instances by default. Backend log ingestion is capped by `log.write`, has short operational retention, truncates large log values, and drops whole log batches when the target repo would exceed daily or burst log limits. The scheduled handler also removes stale static assets, expired app suspensions, old usage records, and stale runtime scripts. ## Policy overrides Limit policies are platform-owned. Apps cannot raise or lower their own limits through `w7s.json`. W7S reads optional policy override records in this order: ```text usage_limit_policy:v1:owner: usage_limit_policy:v1:owner_environment:: usage_limit_policy:v1:repo:: usage_limit_policy:v1:repo_environment::: usage_limit_policy:v1:owner_total: usage_limit_policy:v1:owner_total_environment:: usage_limit_policy:v1:global usage_limit_policy:v1:global_environment: ``` The first four scopes tune a repo's own daily policy. `owner_total`, `owner_total_environment`, `global`, and `global_environment` tune aggregate guardrails. Later records override earlier records within their own policy family. Policy record shape: ```json { "version": 1, "metrics": { "workflow.create": { "dailyUnits": 5000, "warningThreshold": 0.7 }, "queue.send": 25000 }, "updatedAt": "2026-05-26T00:00:00.000Z" } ``` A number is shorthand for `dailyUnits`. `warningThreshold` must be greater than `0` and less than or equal to `1`. Unknown metrics are ignored. W7S operators can manage these records from the core repo: ```sh npm run limits:get -- --owner w7s-io --repo example-workflows ``` ```sh npm run limits:set -- \ --scope repo \ --owner w7s-io \ --repo example-workflows \ --metric workflow.create \ --daily-units 5000 \ --warning-threshold 0.7 ``` ```sh npm run limits:delete -- \ --scope repo \ --owner w7s-io \ --repo example-workflows \ --metric workflow.create ``` ## Current limits caveat Usage rollups are best-effort counters stored by W7S. They are useful for visibility, support, and planning quotas. They are not billing-grade yet. Concurrent events can be approximate, and platform telemetry can arrive late, so enforcement is intentionally conservative and should be treated as free-tier protection rather than exact billing. Metrics marked `platform_estimated` are visible for warnings but are not used to suspend apps. --- # Observability Source: https://w7s.io/docs/observability/ W7S exposes two observability streams: - platform analytics from W7S core events; - backend `console.*`, uncaught exceptions, and non-OK outcomes captured by W7S-managed log capture. ## Analytics API Read per-repository platform analytics with a GitHub token that can access the repo: ```sh curl "https://w7s.cloud/api/v1/analytics//?hours=24&limit=50" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` Query parameters: ```text environment W7S environment, defaults to production hours trailing lookback window, 1 to 168, defaults to 24 from optional ISO timestamp, overrides hours start to optional ISO timestamp, defaults to now bucket hour or day, defaults to hour limit recent event limit, 1 to 200, defaults to 50 ``` The response includes: - `summary`: aggregate counts by event and outcome. - `timeseries`: counts by event and hour or day. - `events`: recent platform events with source, target, method, status, and duration. If Analytics Engine is not configured, the endpoint returns `configured: false` with empty arrays. ## Backend logs API Read recent per-repository backend logs with the same kind of GitHub token: ```sh curl "https://w7s.cloud/api/v1/logs//?hours=1&limit=100" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` For example: ```sh curl "https://w7s.cloud/api/v1/logs/w7s-io/example-logs?hours=1&limit=20" \ -H "Authorization: Bearer $GITHUB_TOKEN" ``` Query parameters: ```text environment W7S environment, defaults to production hours trailing lookback window, 1 to 168, defaults to 1 from optional ISO timestamp, overrides hours start to optional ISO timestamp, defaults to now kind console, exception, or outcome level debug, info, log, warn, or error limit record limit, 1 to 500, defaults to 100 cursor opaque cursor from a previous response ``` The response includes `records` ordered newest first. Console records include `level`, structured `message`, and flattened `text`. Exception records include `exception.name`, `exception.message`, and `exception.stack` when the runtime provides one. Example response: ```json { "status": "success", "data": { "logs": { "repository": "w7s-io/example-logs", "environment": "production", "from": "2026-05-26T13:00:00.000Z", "to": "2026-05-26T14:00:00.000Z", "limit": 20, "cursor": null, "records": [ { "version": 1, "id": "log_abc123", "kind": "exception", "timestamp": "2026-05-26T13:42:10.534Z", "observedAt": "2026-05-26T13:42:11.001Z", "environment": "production", "orgSlug": "w7s-io", "repoSlug": "example-logs", "repository": "w7s-io/example-logs", "scriptName": "w7s-io--example-logs--production--6cc5bc0", "outcome": "exception", "level": "error", "text": "Error: example-logs intentional exception", "exception": { "name": "Error", "message": "example-logs intentional exception", "stack": "Error: example-logs intentional exception\n at Object.fetch (...)" }, "request": { "method": "GET", "path": "/throw", "status": 500, "colo": "IAD" } }, { "version": 1, "id": "log_def456", "kind": "console", "timestamp": "2026-05-26T13:41:58.112Z", "observedAt": "2026-05-26T13:41:58.600Z", "environment": "production", "orgSlug": "w7s-io", "repoSlug": "example-logs", "repository": "w7s-io/example-logs", "scriptName": "w7s-io--example-logs--production--6cc5bc0", "outcome": "ok", "level": "warn", "message": [ "example-logs warning", { "path": "/warn" } ], "text": "example-logs warning {\"path\":\"/warn\"}", "request": { "method": "GET", "path": "/warn", "status": 200, "colo": "IAD" } } ] } } } ``` ## Backend logs in GitHub Actions Use `w7s-io/w7s-cloud@v1` with `logs-check-only: true` to fetch recent logs into a workflow run without deploying: ```yaml name: Logs on: workflow_dispatch: permissions: contents: read jobs: logs: runs-on: ubuntu-latest steps: - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} logs-check-only: true logs-hours: 1 logs-limit: 25 ``` The action prints a compact log table in the job output and adds a `W7S Logs` section to the GitHub Actions step summary. Optional filters: ```yaml with: token: ${{ github.token }} logs-check-only: true logs-kind: exception logs-limit: 10 ``` Log records are retained for a short operational window. The default W7S retention is seven days. W7S truncates large log values and applies `log.write` daily and burst limits before storing log records, so runaway logging is dropped instead of becoming a platform cost burn. JavaScript/TypeScript native backends deployed before this feature need to be redeployed once so their deployment metadata includes W7S log capture. --- # URLs And Routing Source: https://w7s.io/docs/urls-and-routing/ W7S maps GitHub repositories to public URLs on `w7s.cloud`. ## Repository paths A repository named: ```text github.com/owner/repo ``` is served at: ```text https://owner.w7s.cloud/repo/ ``` Example: ```text https://w7s-io.w7s.cloud/docs/ ``` ## Branch environments `main` and `master` deploy to `production`, which uses the owner host. Other branches deploy to a branch environment and use a branch-prefixed host: ```text https://--owner.w7s.cloud/repo/ ``` The branch environment is DNS-safe. W7S lowercases the branch name, replaces runs of characters outside `a-z`, `0-9`, and `-` with `-`, collapses repeated hyphens, trims leading/trailing hyphens, and caps the result at 63 characters. For example: ```text feature/API.v2_test -> feature-api-v2-test https://feature-api-v2-test--owner.w7s.cloud/repo/ ``` ## Owner roots If the repository has the same name as the owner: ```text github.com/owner/owner ``` it can serve the owner root: ```text https://owner.w7s.cloud/ ``` This is useful for personal or organization homepages. ## Missing routes When W7S cannot find a deployment for a requested owner or repository path, it shows a deploy help page with the exact GitHub repository needed for that URL. For example: ```text https://sadasant.w7s.cloud/example/ ``` points at: ```text https://github.com/sadasant/example ``` ## Custom-domain only routing If your deployment has a [`CNAME`](https://w7s.io/docs/custom-domains/) custom domain, you can disable the default `w7s.cloud` route: ```json title="w7s.json" { "routing": { "defaultDomain": false } } ``` When this is set, the deployment only serves through custom domains declared in [`CNAME`](https://w7s.io/docs/custom-domains/). The deploy fails if no custom domain attaches successfully. Use this when a production app should have one canonical origin, such as: ```text https://app.example.com/ ``` instead of also being reachable at: ```text https://owner.w7s.cloud/repo/ ``` --- # Custom Domains Source: https://w7s.io/docs/custom-domains/ Add a [`CNAME`](https://w7s.io/docs/custom-domains/) file to claim one or more custom hostnames for a deployment. ```text title="CNAME" fullstack-example.w7s.io www.fullstack-example.w7s.io ``` W7S reads [`CNAME`](https://w7s.io/docs/custom-domains/) from the deployed archive. Common locations include: ```text CNAME frontend/CNAME frontend/dist/CNAME dist/CNAME build/CNAME out/CNAME ``` ## DNS Create DNS for each hostname and point it at W7S. For a subdomain, create a proxied [CNAME](https://w7s.io/docs/custom-domains/): ```text Type: CNAME Name: fullstack-example Target: w7w.cloud Proxy: enabled ``` ## TXT security allowlist The first custom-domain claim can work without TXT verification. W7S still recommends adding a TXT record to restrict future claims. For a host under `w7s.io`, add: ```text Type: TXT Name: _w7s.w7s.io Value: w7s-io/example-fullstack-ts ``` TXT values can list owners or exact repositories: ```text w7s-io w7s-io/docs w7s-io/docs,guerrerocarlos ``` If multiple repositories try to claim the same hostname, the TXT allowlist decides which repository is allowed. ## Custom-domain only By default, W7S serves a deployment from both its default `w7s.cloud` URL and any custom domain declared in [`CNAME`](https://w7s.io/docs/custom-domains/). For production apps that should only be reachable from your own hostname, add `routing.defaultDomain=false` to `w7s.json`: ```json title="w7s.json" { "routing": { "defaultDomain": false } } ``` With this setting, the deployment must include a [`CNAME`](https://w7s.io/docs/custom-domains/) file and at least one custom domain must attach successfully. Requests to the default `w7s.cloud` URL will behave as if the deployment is not present. This is recommended when you want one canonical origin for cookies, browser storage, CSP, redirects, and application security policy. --- # Deploy API Source: https://w7s.io/docs/deploy-api/ Most users should deploy with `w7s-io/w7s-cloud@v1`. The deploy action calls the W7S deploy API. ## Endpoint ```text POST https://w7s.cloud/api/v1/deploy ``` The request body is a zip archive. ## Headers ```text Authorization: Bearer x-github-repository: owner/repo x-github-sha: x-github-branch: content-type: application/zip ``` `application/octet-stream` is also accepted. Optional runtime value headers are base64url-encoded JSON objects: ```text x-w7s-vars: x-w7s-secrets: ``` The official GitHub Action writes these headers from `w7s.json` and the workflow environment. ## Authentication W7S checks the token against GitHub: ```text GET https://api.github.com/repos/owner/repo Authorization: Bearer ``` If GitHub returns `401`, `403`, or `404`, W7S rejects the deploy. ## Environments By default: - `main` and `master` deploy to `production`. - Other branches deploy to a sanitized branch environment. Environment names are DNS-safe. W7S lowercases the branch or explicit override, replaces runs of characters outside `a-z`, `0-9`, and `-` with `-`, collapses repeated hyphens, trims leading/trailing hyphens, and caps the result at 63 characters. Production deployments are served from the owner host: ```text https://.w7s.cloud// ``` Non-production branch deployments are served from a branch-prefixed host: ```text https://--.w7s.cloud// ``` For example, branch `feature/API.v2_test` becomes environment `feature-api-v2-test`: ```text https://feature-api-v2-test--owner.w7s.cloud/repo/ ``` You can override the environment with either: ```text ?environment=staging x-w7s-environment: staging ``` ## Deployable Outputs Native backend entrypoints are JavaScript or TypeScript runtime modules only: ```text backend/index.js backend/index.mjs backend/index.ts backend/index.mts worker/index.js worker/index.mjs worker/index.ts worker/index.mts dist/server/index.js dist/server/index.mjs ``` Static frontend roots: ```text frontend/dist/ frontend/build/ frontend/out/ dist/client/ dist/ build/ out/ ``` Static roots normally need an `index.html`. `dist/client/` may be asset-only when paired with `dist/server/index.js`, which is the output produced by TanStack Start and similar SSR builds. If `backend/`, `worker/`, or `dist/server/` exists but does not contain a supported JavaScript or TypeScript entrypoint, W7S still deploys a valid static frontend and returns a `deploymentWarnings` entry explaining that the backend was skipped. If there is no deployable frontend, the archive is rejected. See [Project Layouts](./project-layouts.md#native-backend-function-shape) for the native backend handler contract and a Hono example. ## Free-tier shape caps W7S rejects deploys that are too large or declare too many resources before anything is published: ```text archive zip bytes 25 MB uncompressed bytes 100 MB static files 1000 static total bytes 100 MB static single file 10 MB Key-value bindings 3 FS bindings 3 SQL bindings 2 Stateful object classes 2 queues 2 schedules 5 workflows 5 custom domains 3 SQL migration files 50 SQL migration bytes 5 MB ``` Native backends are dispatched with a custom CPU limit from `W7S_USER_WORKER_CPU_MS`, default `50`, and a subrequest limit from `W7S_USER_WORKER_SUBREQUESTS`, default `25`. Native backend uploads include W7S-managed log capture unless the platform operator disables it. See [Observability](./observability.md) for the logs API. ## App manifest JavaScript/TypeScript native backends can include a `w7s.json` manifest to declare platform resources: ```json { "bindings": { "kv": ["CACHE"], "fs": ["FILES"], "db": [{ "binding": "DB", "migrations": "migrations" }], "durableObjects": [ { "binding": "COUNTER", "className": "Counter" } ], "hyperdrive": [ { "binding": "DB", "id": "postgres-binding-id" } ] }, "queues": ["jobs"], "schedules": [ { "cron": "*/5 * * * *", "path": "/_w7s/schedules/sync" } ], "rpc": { "allow": ["another-owner/client"] }, "queue": { "allow": ["another-owner/client"] }, "vars": ["PUBLIC_API_KEY"], "secrets": ["PRIVATE_API_KEY"] } ``` `queues` declares managed background queues for the app. A string queue declaration uses the default consumer path `/_w7s/queues/`. Use an object to set a custom consumer path: ```json { "queues": [ { "name": "jobs", "consumer": "/internal/queues/jobs" } ] } ``` `bindings.durableObjects` declares stateful object classes exported by the JavaScript/TypeScript native backend. W7S creates persistent classes when first deployed. See [Stateful Objects](./backend-durable-objects.md) for examples. `bindings.hyperdrive` declares existing managed Postgres binding configs by ID. W7S exposes them to the backend at the configured binding name. See [Postgres Bindings](./backend-hyperdrive.md) for examples. See [Storage Bindings](./storage-bindings.md), [Backend AI](./backend-ai.md), [Backend RPC](./backend-rpc.md), and [Backend Queues](./backend-queues.md) for the runtime behavior of the other declarations. `schedules` declares cron-driven backend jobs. Each entry has a five-field UTC cron expression and an absolute backend path. See [Backend Schedules](./backend-schedules.md) for examples. ## Usage API W7S also exposes per-app daily usage rollups with daily limits and warning thresholds: ```text GET https://w7s.cloud/api/v1/usage//?date=YYYY-MM-DD Authorization: Bearer ``` Effective limit policies are available without usage counters: ```text GET https://w7s.cloud/api/v1/limits// Authorization: Bearer ``` The token must have access to the GitHub repository, just like deploys. See [Usage Accounting](./usage-accounting.md) for the response shape and current limits. --- # Examples Source: https://w7s.io/docs/examples/ Use these repositories as starting points. ## Static Site - GitHub: [w7s-io/example-static-site](https://github.com/w7s-io/example-static-site) - W7S URL: [w7s-io.w7s.cloud/example-static-site](https://w7s-io.w7s.cloud/example-static-site/) This example demonstrates a static-only deploy with `dist/index.html` and no build step. See [Project Layouts](./project-layouts.md#static-frontends) for the static frontend output roots W7S detects. ## Native Backend - GitHub: [w7s-io/example-native-backend](https://github.com/w7s-io/example-native-backend) - Live health endpoint: [w7s-io.w7s.cloud/example-native-backend/api/health](https://w7s-io.w7s.cloud/example-native-backend/api/health) - Live greeting endpoint: [w7s-io.w7s.cloud/example-native-backend/api/greeting](https://w7s-io.w7s.cloud/example-native-backend/api/greeting) This example demonstrates a single JavaScript/TypeScript native backend module that default-exports a `fetch(request, env, ctx)` handler. See [Project Layouts](./project-layouts.md#native-backend-function-shape) for the backend contract and Hono example. ## Fullstack TypeScript - GitHub: [w7s-io/example-fullstack-ts](https://github.com/w7s-io/example-fullstack-ts) - W7S URL: [w7s-io.w7s.cloud/example-fullstack-ts](https://w7s-io.w7s.cloud/example-fullstack-ts/) - Custom domain: [fullstack-example.w7s.io](https://fullstack-example.w7s.io/) This example demonstrates a bundled Hono backend, a React frontend, and a [`frontend/CNAME`](https://w7s.io/docs/custom-domains/) custom-domain claim deployed together through W7S. ## AI Joke Generator - GitHub: [w7s-io/example-ai-joke](https://github.com/w7s-io/example-ai-joke) - W7S URL: [w7s-io.w7s.cloud/example-ai-joke](https://w7s-io.w7s.cloud/example-ai-joke/) - Backend health endpoint: [w7s-io.w7s.cloud/example-ai-joke/api/status](https://w7s-io.w7s.cloud/example-ai-joke/api/status) This example demonstrates a Vite React frontend, a Hono backend, and a backend-only call through the W7S AI service binding. The browser calls `/api/joke`; the backend calls `env.W7S_AI` with a W7S-generated deployment token, so the repo does not need provider account IDs, provider API tokens, or GitHub secrets. See [Backend AI](./backend-ai.md) for the request shape. ## Backend RPC - Target service: [w7s-io/example-rpc-datetime](https://github.com/w7s-io/example-rpc-datetime) - Client service: [w7s-io/example-rpc-client](https://github.com/w7s-io/example-rpc-client) - Live client endpoint: [w7s-io.w7s.cloud/example-rpc-client/datetime](https://w7s-io.w7s.cloud/example-rpc-client/datetime) The client exposes a public `/datetime` endpoint and gets the current datetime from the target service through `env.W7S_RPC`. Its GitHub Actions workflow includes a smoke test that verifies the response came through W7S RPC. See [Backend RPC](./backend-rpc.md) for copy-pasteable examples showing: - a target backend route; - a caller backend using `env.W7S_RPC`; - a reusable RPC helper; - same-owner and cross-owner authorization. Local `w7s-local` RPC examples: - Target service: [`examples/w7s-local-rpc-time-service`](https://github.com/w7s-io/docs/tree/main/examples/w7s-local-rpc-time-service) - Client service: [`examples/w7s-local-rpc-client`](https://github.com/w7s-io/docs/tree/main/examples/w7s-local-rpc-client) These examples run two local W7S routers on different ports and let the client call the target through the target's local W7S URL. See [Local Development](./local-development.md#test-rpc-between-two-local-repos) for the terminal commands. ## Stateful Objects - GitHub: [w7s-io/example-durable-counter](https://github.com/w7s-io/example-durable-counter) - Live app endpoint: [w7s-io.w7s.cloud/example-durable-counter](https://w7s-io.w7s.cloud/example-durable-counter/) - Live current value endpoint: [w7s-io.w7s.cloud/example-durable-counter/value](https://w7s-io.w7s.cloud/example-durable-counter/value) This example declares a `Counter` stateful object in `w7s.json`, exports the `Counter` class from `backend/index.js`, and verifies persisted state across W7S redeploys. See [Stateful Objects](./backend-durable-objects.md) for copy-pasteable examples showing: - stateful object declaration in `w7s.json`; - exporting the stateful object class; - routing requests through the generated binding; - redeploy behavior. ## Serverless Database - GitHub: [w7s-io/example-serverless-database](https://github.com/w7s-io/example-serverless-database) - Live app endpoint: [w7s-io.w7s.cloud/example-serverless-database](https://w7s-io.w7s.cloud/example-serverless-database/) - Live notes endpoint: [w7s-io.w7s.cloud/example-serverless-database/api/notes](https://w7s-io.w7s.cloud/example-serverless-database/api/notes) This example declares a W7S serverless DB in `w7s.json`, stores plain SQL migrations in `migrations/`, and queries the database from a JavaScript/TypeScript native backend. See [Serverless Database](./serverless-database.md) for copy-pasteable examples showing: - database declaration in `w7s.json`; - sorted SQL migrations; - schema and query code; - deployment workflow and smoke test shape. ## Postgres Bindings - GitHub: [w7s-io/example-postgres-binding](https://github.com/w7s-io/example-postgres-binding) This example demonstrates the source, build, runtime compatibility metadata, and `w7s.json` shape for a backend that reads a W7S-managed Postgres binding from `env.DB`. It is manual-deploy because a real external database and managed Postgres binding config ID are required. The W7S binding shape is: ```json { "bindings": { "hyperdrive": [ { "binding": "DB", "id": "postgres-binding-id" } ] } } ``` See [Postgres Bindings](./backend-hyperdrive.md) for the full setup flow. ## Backend Queues Single backend example: - GitHub: [w7s-io/example-queue-worker](https://github.com/w7s-io/example-queue-worker) - Live enqueue endpoint: [w7s-io.w7s.cloud/example-queue-worker/enqueue](https://w7s-io.w7s.cloud/example-queue-worker/enqueue) - Live latest message endpoint: [w7s-io.w7s.cloud/example-queue-worker/last](https://w7s-io.w7s.cloud/example-queue-worker/last) This example declares a `jobs` queue, sends JSON messages through `env.W7S_QUEUE.fetch("https://w7s.internal/api/v1/queues/w7s-io/example-queue-worker/jobs")`, and consumes the batch at `/_w7s/queues/jobs`. Separate producer and consumer example: - Producer GitHub: [w7s-io/example-queue-producer](https://github.com/w7s-io/example-queue-producer) - Consumer GitHub: [w7s-io/example-queue-consumer](https://github.com/w7s-io/example-queue-consumer) - Live producer endpoint: [w7s-io.w7s.cloud/example-queue-producer/enqueue](https://w7s-io.w7s.cloud/example-queue-producer/enqueue) - Live consumer latest message endpoint: [w7s-io.w7s.cloud/example-queue-consumer/last](https://w7s-io.w7s.cloud/example-queue-consumer/last) The producer sends to `https://w7s.internal/api/v1/queues/w7s-io/example-queue-consumer/jobs`. The consumer declares the queue in `w7s.json`, receives batches at `/_w7s/queues/jobs`, and stores processed messages in key-value storage. See [Backend Queues](./backend-queues.md) for copy-pasteable examples showing: - queue declaration in `w7s.json`; - sending messages through the internal queue binding; - implementing the backend consumer route; - splitting producer and consumer into separate backends; - same-owner and cross-owner authorization. ## W7S-Native Events Without NATS - Local example: [`examples/w7s-local-native-events`](https://github.com/w7s-io/docs/tree/main/examples/w7s-local-native-events) - Related article: [Replacing NATS With W7S Components](https://w7s.io/docs/blog/replacing-nats-with-w7s-components) This example turns the event-router pattern from the article into three local repos that can be run with `w7s-local`: an `order-api` publisher, an `event-router` fanout service, and an `email-worker` queue consumer. It demonstrates how to test W7S-native event fanout locally without running NATS or another broker. The publisher calls the router through a W7S-shaped local URL, and the router delivers a queue-shaped batch to the subscriber. ## Backend Schedules - GitHub: [w7s-io/example-schedules](https://github.com/w7s-io/example-schedules) - Live app endpoint: [w7s-io.w7s.cloud/example-schedules](https://w7s-io.w7s.cloud/example-schedules/) - Live latest tick endpoint: [w7s-io.w7s.cloud/example-schedules/last](https://w7s-io.w7s.cloud/example-schedules/last) This example declares a `* * * * *` schedule in `w7s.json`, receives the scheduled job at `/_w7s/schedules/tick`, and stores the latest schedule payload in key-value storage. The GitHub Actions workflow polls `/last` until a fresh scheduled tick arrives after deployment. See [Backend Schedules](./backend-schedules.md) for copy-pasteable examples showing: - schedule declaration in `w7s.json`; - implementing the backend schedule route; - persisting schedule results in key-value storage; - schedule environment behavior. ## Backend Workflows - GitHub: [w7s-io/example-workflows](https://github.com/w7s-io/example-workflows) - Live app endpoint: [w7s-io.w7s.cloud/example-workflows](https://w7s-io.w7s.cloud/example-workflows/) - Live latest workflow endpoint: [w7s-io.w7s.cloud/example-workflows/last](https://w7s-io.w7s.cloud/example-workflows/last) This example declares a `process-order` workflow in `w7s.json`, starts workflow instances through `env.W7S_WORKFLOW.fetch("https://w7s.internal/api/v1/workflows/w7s-io/example-workflows/process-order")`, and receives each workflow run at `/_w7s/workflows/process-order`. See [Backend Workflows](./backend-workflows.md) for copy-pasteable examples showing: - workflow declaration in `w7s.json`; - starting workflow instances through the internal workflow binding; - implementing the backend workflow route; - checking instance status; - same-owner and cross-owner authorization. ## Usage Check - GitHub: [w7s-io/example-usage-check](https://github.com/w7s-io/example-usage-check) - Live app endpoint: [w7s-io.w7s.cloud/example-usage-check](https://w7s-io.w7s.cloud/example-usage-check/) This example deploys a tiny static app and includes a separate scheduled workflow that runs `w7s-io/w7s-cloud@v1` with `usage-check-only: true`. That reads usage and limit status without redeploying the app. See [Usage Accounting](./usage-accounting.md) for the API response shape and limit behavior. ## Observability - GitHub: [w7s-io/example-logs](https://github.com/w7s-io/example-logs) - Live app endpoint: [w7s-io.w7s.cloud/example-logs](https://w7s-io.w7s.cloud/example-logs/) - Warning endpoint: [w7s-io.w7s.cloud/example-logs/warn](https://w7s-io.w7s.cloud/example-logs/warn) - Error endpoint: [w7s-io.w7s.cloud/example-logs/error](https://w7s-io.w7s.cloud/example-logs/error) This example emits `console.log`, `console.warn`, `console.error`, and an intentional exception. It also includes manual GitHub Actions workflows for `logs-check-only: true` and the W7S analytics API. See [Observability](./observability.md) for copy-pasteable examples showing: - fetching platform analytics with curl; - fetching logs with `w7s-io/w7s-cloud@v1`; - fetching logs with curl; - the JSON shape returned by the logs API; - filtering by log kind and level. ## Docs site This docs repo is itself deployed through W7S: - GitHub: [w7s-io/docs](https://github.com/w7s-io/docs) - W7S URL: [w7s-io.w7s.cloud/docs](https://w7s-io.w7s.cloud/docs/) - Landing page: [w7s.io](https://w7s.io/) - Docs: [w7s.io/docs](https://w7s.io/docs/) - Redirect: [www.w7s.io](https://www.w7s.io/) It builds the landing frontend from `landing/`, builds the docs with Docusaurus, assembles them into the generated `build/` directory, and deploys with `w7s-io/w7s-cloud@v1`. The repo uses the same GitHub Actions deployment flow documented here: install dependencies, build the site, then deploy with the W7S action. ```yaml - uses: w7s-io/w7s-cloud@v1 with: token: ${{ github.token }} ``` Its [`static/CNAME`](https://w7s.io/docs/custom-domains/) file declares the canonical apex host and the `www` redirect host: ```text w7s.io www.w7s.io ``` The repo also ships a small `backend/index.ts` worker that redirects `www.w7s.io` to `https://w7s.io/`.