# banchi technical setup manual

Documentation and provider specifications checked: 2026-09-10. Live-test dates are listed under [Evidence](#evidence).

[Connection guide](https://banchi.app/guide-en) · [日本語](https://banchi.app/technical-guide.md) · [AI documentation index](https://banchi.app/llms.txt)

For developers and AI assistants connecting an existing service to a rented address. `your-name.banchi.app` is a placeholder: replace it with your own rented hostname and use the target values from your hosting provider. Reading this document does not change DNS, servers or contracts.

For AI-assisted setup, read the [connection Skill](https://banchi.app/skills/banchi-connect/SKILL.md). Identify the actual hosting product and production deployment, open its current official documentation during the task, and report source URLs, date accessed and costs before setup. If the deployment, current sources or additional costs cannot be confirmed, stop before dependent changes. A [human-reviewed starter prompt](https://banchi.app/guide-en#ai) is also available.

- [Scope and prerequisites](#requirements)
- [Shared workflow and DNS input](#workflow)
- [GitHub Pages](#github) / [Cloudflare Pages](#cloudflare-pages) / [Vercel](#vercel) / [Netlify](#netlify)
- [AWS Amplify](#amplify) / [Firebase Hosting](#firebase) / [Azure Static Web Apps](#azure) / [Heroku](#heroku) / [DigitalOcean](#digitalocean) / [Google Cloud Run](#google-cloud): six additional services checked against official documentation only
- [Your own server](#own-server): [Caddy](#caddy) / [nginx and Certbot](#nginx) / [Manual DNS-01](#dns01)
- [Workers entry setup and R2 constraints](#workers-r2)
- [DNS and HTTPS verification commands](#checks)
- [Authentication, forms and payments](#app-check)
- [Existing API contract](#api)
- [Migration and removal](#retire) / [Evidence](#evidence)

<a id="requirements"></a>
## 1. Scope and prerequisites

| Item | Meaning |
| --- | --- |
| Management UI and API origin | `https://banchi.app` |
| Example rented address | `your-name.banchi.app`; child names such as `api.your-name.banchi.app` can also be configured |
| Normal connection | DNS-only. Visitors connect directly to your hosting provider |
| banchi provides | Use of the address during your contract and supported DNS configuration |
| You provide | Hosting, application, server, certificates and renewal, hosting costs and traffic protection |
| Price | JPY 500 including tax per address per year, automatically renewed; available to customers in Japan. One email can hold several addresses, with separate contracts and renewal dates |

An existing operator-only free acquisition exception is not a public free plan. See the [terms](https://banchi.app/terms).

Before buying, confirm that your provider accepts a subdomain whose parent zone you do not control, that its DNS requirements fit the limits below, and that your plan allows custom domains. Make the site or API work at the provider's standard URL first. DNS alone does not create the application or HTTPS configuration.

<a id="workflow"></a>
## 2. Shared workflow and DNS input

1. Acquire an address in banchi and open its settings from **Your addresses** after signing in.
2. Register the rented hostname at the hosting provider, without scheme, path or port. Follow provider-specific exceptions such as saving a verification TXT before registering GitHub's traffic domain.
3. A newly acquired address goes directly to **How do you want to use this name?** For an existing address, open **Change how it is used**. Choose **Connect to my own hosting**, select the provider preset, and save its CNAME target or your server's public IP.
4. Use **Edit DNS records directly** for additional names, multiple IPs, web TXT or CAA. Review the entire plan and retain ownership TXT records you still need.
5. Wait for the provider's DNS verification and certificate, run banchi's diagnosis, then complete both [external checks](#checks) and [application checks](#app-check).

| Input | Rules |
| --- | --- |
| Name `@` | The rented hostname itself, not the entire `banchi.app` parent |
| Child name | A complete name such as `api.dev.your-name.banchi.app`; relative `api` alone is not accepted |
| A / AAAA | Reachable public IPv4 / IPv6. One IP also works in the generic simple form; use individual records for multiple IPs |
| CNAME | Only the target hostname supplied by the provider; no URL, port or IP. No other record may coexist at the same name |
| TXT | Web verification text: printable ASCII, at most 2048 bytes. Do not include display-only outer quotes |
| CAA | The actual certificate issuer's setting. `0 issue "letsencrypt.org"` demonstrates syntax, not a recommendation for every provider |

The standard quota is five records per address, including verification TXT, CAA and the system fallback root. Without a user-supplied root A/AAAA/CNAME, banchi adds one fallback root record, leaving four user slots. Adding your root target replaces the fallback automatically; do not edit or submit the system record yourself.

You can save only verification TXT first, or publish child hosts without your own root target. Each child needs its own provider registration and certificate coverage. Email MX/SPF/DKIM/DMARC, NS delegation, wildcard DNS and changes outside your rented namespace are not provided.

<a id="github"></a>
## 3. GitHub Pages

1. Configure a branch or GitHub Actions under repository **Settings → Pages** and check its standard URL. GitHub Free requires a public repository; review tracked files and history for secrets before changing visibility.
2. Under account/organization **Settings → Pages → Add a domain**, verify the rented hostname. Save the exact TXT name and value in banchi, wait for propagation, then select **Verify**. You need not verify the whole parent domain.
3. Save the rented hostname as the repository's **Custom domain** before directing traffic. In banchi's GitHub Pages preset, set the CNAME to `YOUR_GITHUB_USER.github.io` or the organization equivalent, without a repository path. Retain the verification TXT when saving.
4. Enable **Enforce HTTPS** when available; this may take up to 24 hours. Keep the verification TXT.
5. Branch publishing uses the generated `CNAME` file: preserve it during builds. Actions publishing does not require that file; manage the domain in Pages settings.
6. Update static-site base URLs, asset paths and external API origins. Pages does not itself run arbitrary server APIs. Complete [application checks](#app-check).

Sources: [GitHub custom domains](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site), [verification](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/verifying-your-custom-domain-for-github-pages).

<a id="cloudflare-pages"></a>
## 4. Cloudflare Pages

Before deploying, match the framework, build command and output directory to the project. Uploading static files does not migrate an existing server API. Check the [official build configuration](https://developers.cloudflare.com/pages/configuration/build-configuration/) and configure Production Functions settings below where needed.

1. Deploy the project in your own account and verify `YOUR_PROJECT.pages.dev`.
2. First add the rented hostname under that project's **Custom domains → Set up a custom domain**. Do not add `banchi.app` as your own zone or change nameservers.
   If the same Cloudflare account also controls the parent zone, Pages can create a CNAME after confirmation. Inspect the proposed change before confirming; stop if it would directly overwrite DNS managed by banchi. In the normal separate-account path, save the CNAME through banchi in the next step.
3. Save the supplied CNAME target in banchi's Cloudflare Pages preset. Creating only the CNAME without registering it in Pages can produce a 522 error.
4. Confirm the domain and certificate are active. If you added CAA, ensure it permits the provider's actual certificate issuer.
5. For Functions, configure Production variables, secrets and bindings, redeploy, and test the API at the new hostname. Verified cross-account DNS does not automatically migrate application code.

Sources: [Pages custom domains](https://developers.cloudflare.com/pages/configuration/custom-domains/), [Functions bindings](https://developers.cloudflare.com/pages/functions/bindings/). Also read [Workers and R2](#workers-r2) before migrating code.

<a id="vercel"></a>
## 5. Vercel

First review Root Directory, Framework Preset, Build Command and Output Directory under **Settings → Build and Deployment**. Deploy and test the production default URL. Keep settings appropriate to your framework and deploy APIs through a supported Functions setup. See [build configuration](https://vercel.com/docs/builds/configure-a-build). Hobby is for personal, non-commercial use; check [plan conditions](https://vercel.com/docs/plans/hobby) for commercial use.

1. Add the rented hostname under project **Settings → Domains → Add Domain**.
2. Copy that project's specific CNAME target into banchi's Vercel preset. Do not reuse another project's value or an apex-domain A record.
3. Add ownership TXT only if requested. Compare the complete name with the preset's generated name; use individual DNS if they differ. Contact support if a mandatory record is outside your rented namespace.
4. Wait for valid domain configuration and HTTPS in Vercel, then run banchi's diagnosis.
5. Update Production public URLs, authentication callbacks and external API origins, redeploy where needed, and run [application checks](#app-check). Keep preview and production settings distinct.

Do not select nameserver or wildcard configuration. Source: [Vercel domain setup](https://vercel.com/docs/domains/working-with-domains/add-a-domain).

<a id="netlify"></a>
## 6. Netlify

First select the repository and production branch, check the build command and publish directory, then deploy and test the default URL. Deploy Functions and their environment variables through the supported Functions setup, not inside the public static directory. Follow the [build settings](https://docs.netlify.com/build/configure-builds/overview/) and never publish the whole repository if it contains secrets.

1. Add the hostname under **Domain management / Production domains → Add a domain you already own**. Use external DNS, not a nameserver move to Netlify DNS.
2. Save the supplied CNAME, such as `YOUR_SITE.netlify.app`, in banchi's Netlify preset.
3. Parent-ownership notices were optional in our 2026-09-07 test. If your current screen requires verification, check that the requested record is within your rented namespace. Do not ignore a mandatory TXT at the parent `banchi.app`; contact support.
4. Check DNS verification and certificate issuance under Netlify **HTTPS**. Retain the registration, DNS and required verification for renewal.
5. Update the Production site URL, Functions variables and form/authentication allowed URLs. Redeploy as needed and complete [application checks](#app-check).

Sources: [Netlify external DNS](https://docs.netlify.com/manage/domains/configure-domains/configure-external-dns/), [HTTPS](https://docs.netlify.com/manage/domains/secure-domains-with-https/https-ssl/).

<a id="own-server"></a>
## 7. Your own server: VPS, cloud VM or a suitable home connection

A usual setup needs a public IP, inbound TCP ports 80/443, a running application, and a certificate-managing web server. Check cloud firewall, OS firewall and router/NAT separately. DNS changes do not overcome CGNAT or an ISP's inbound restrictions.

Home hosting is not categorically excluded. However, a global IPv6 address alone does not establish reachability. End-to-end HTTPS to a reachable IPv6 server has not been demonstrated through banchi. Do not publish an unreachable AAAA. If using both A and AAAA, both paths must serve the intended site.

The examples below assume an existing web app listening at `127.0.0.1:3000`. DNS does not contain `:3000`: a web frontend accepts public 80/443 and proxies to the application. Configure the app to accept your hostname too.

Use your server's existing software-management policy. On NixOS, prefer declarative service configuration instead of having Certbot edit generated files. These examples assume Caddy/nginx/Certbot are already installed; they do not prescribe installation or overwriting existing configuration. Do not run two web servers on the same ports.

<a id="caddy"></a>
### 7.1 Caddy

1. Save a root A or AAAA pointing to your server's public IP and [check DNS](#checks).
2. Add this site block without removing existing sites. This is a reverse-proxy example, distinct from the small response page used in banchi's live test.

```caddyfile
your-name.banchi.app {
    reverse_proxy 127.0.0.1:3000
}
```

3. Validate on the server, adjusting the path to your configuration.

```sh
caddy validate --config ./Caddyfile --adapter caddyfile
```

4. Apply through your existing service-management procedure. For a CLI-managed running Caddy, the following changes its active configuration:

```sh
caddy reload --config ./Caddyfile --adapter caddyfile
```

If no service exists, configure a persistent OS-managed service. Do not rely on a foreground development process that ends when you close the terminal. Caddy normally validates through HTTP-01 on port 80 or TLS-ALPN-01 on port 443. Keep certificate storage writable and persistent across restarts/redeployments, and maintain DNS and reachability. Check issuance logs and external HTTPS.

Sources: [Caddy automatic HTTPS](https://caddyserver.com/docs/automatic-https), [reverse proxy](https://caddyserver.com/docs/quick-starts/reverse-proxy), [validation and reload](https://caddyserver.com/docs/command-line).

<a id="nginx"></a>
### 7.2 nginx and Certbot

This combination has not had a banchi end-to-end test. The example is for administrators of an existing setup. Point A/AAAA to the public IP, then add a nonconflicting site block to a configuration included inside nginx's `http` context.

```nginx
server {
    listen 80;
    server_name your-name.banchi.app;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

Publishing AAAA also needs IPv6 listeners, such as `listen [::]:80;`, plus working port 443. Limit the application's trusted proxies to those you control. WebSockets need additional configuration; this is not a complete configuration for every protocol.

Validate first. Reload only after validation succeeds, using your existing service permissions and procedure.

```sh
nginx -t && nginx -s reload
```

If the Certbot nginx plugin supports your setup, this command requests a certificate and edits nginx configuration. **It contacts a certificate authority and changes configuration.** Back up the configuration and review the interactive prompts.

```sh
certbot --nginx -d your-name.banchi.app
```

Check port 443, HTTP-to-HTTPS redirection and external HTTPS. Renewal requires the original plugin, a working timer/cron, HTTP-01 reachability, and loading renewed certificates into nginx. Inspect your OS's existing timer and logs; installation alone does not prove scheduling.

```sh
certbot renew --dry-run
```

This exercises renewal against a staging service and may execute configured hooks; it is not a purely read-only command. Future DNS, scheduling and network conditions must remain valid.

Sources: [nginx configuration](https://nginx.org/en/docs/beginners_guide.html), [commands](https://nginx.org/en/docs/switches.html), [proxy headers](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header), [WebSockets](https://nginx.org/en/docs/http/websocket.html), [Certbot usage and renewal](https://eff-certbot.readthedocs.io/en/stable/using.html).

<a id="dns01"></a>
### 7.3 Manual DNS-01

Prefer server-managed HTTP-01 or similar where possible. DNS-01 also works for non-wildcard certificates, but manual DNS does not provide unattended renewal.

1. Start DNS-01 validation in your own ACME client and obtain its exact TXT name and value.
2. Save that full name, usually `_acme-challenge.your-name.banchi.app`, in banchi's individual DNS editor. Preserve other records and check the five-record quota and same-name CNAME conflicts.
3. Check the TXT through both public resolvers before continuing the ACME challenge.
4. Install the certificate on your own web server and verify HTTPS. After validation, remove only the now-unneeded challenge TXT. Renewal needs a fresh value and another validation step.

Certbot's manual plugin does not renew unattended without custom hooks. banchi supplies neither certificate-renewal API tokens nor managed challenge delegation. CNAME delegation to another challenge DNS service exists as a technique, but needs CA/client support and separately operated DNS. It is not a tested or provided banchi automation path. NS delegation and wildcard DNS are also unavailable.

Sources: [Let's Encrypt challenges](https://letsencrypt.org/docs/challenge-types/), [Certbot manual plugin](https://eff-certbot.readthedocs.io/en/stable/using.html#manual).

<a id="workers-r2"></a>
## 8. Cloudflare Workers: add a Pages entry point

**Keep your existing Worker and add one small Pages project in the same Cloudflare account.** Traffic flows `visitor → your Pages entry → your Worker`. banchi provides DNS and does not relay your content, cookies or APIs.

Standard Workers Custom Domains require an account-owned zone. Pages can register a subdomain managed in another account, so use this Pages project as the CNAME target instead of `workers.dev`. You need neither a separate parent domain nor to share a Cloudflare API key with banchi.

### 1. Check the existing Worker and what it exposes

- Verify the site, API and authentication at the existing Worker's standard URL. Pages and the Worker must be in **the same account**.
- Keep application code, D1, KV, R2, DO, secrets, Cron and Queues in the Worker. Nothing needs to be copied or recreated. The entry calls its HTTP `fetch` handler.
- **If Cloudflare Access or WAF on the old URL is your only protection, resolve that before connecting.** Service bindings bypass that public URL, and `ctx.access` is not propagated downstream. Private pages and administrative APIs must authenticate inside the Worker, with appropriate protection for the new entry.

### 2. Create two entry files

Create `pages-entry/` within the existing repository without overwriting the Worker's configuration. The following two files need no build step. Put only the entry `_worker.js` in `dist/`; do not copy application assets, `.env` or secrets there.

`pages-entry/dist/_worker.js`:

```js
export default {
  fetch(request, env) {
    if (new URL(request.url).origin !== env.PUBLIC_ORIGIN) {
      return new Response('Not found', { status: 404 });
    }
    if (!env.APP) {
      return new Response('Service unavailable', {
        status: 503, headers: { 'Cache-Control': 'no-store' },
      });
    }
    return env.APP.fetch(request);
  },
};
```

`pages-entry/wrangler.jsonc`:

```json
{
  "name": "my-worker-entry",
  "pages_build_output_dir": "./dist",
  "compatibility_date": "2026-09-09",
  "vars": {},
  "services": [],
  "env": {
    "production": {
      "vars": { "PUBLIC_ORIGIN": "https://your-name.banchi.app" },
      "services": [{ "binding": "APP", "service": "existing-worker" }]
    },
    "preview": {
      "vars": { "PUBLIC_ORIGIN": "" },
      "services": []
    }
  }
}
```

Replace the new Pages name `my-worker-entry`, rented HTTPS origin `https://your-name.banchi.app` (no trailing `/`), and **deployed Worker name** `existing-worker`. The service value is a name, not a URL. For an environment-specific Worker, use its actual deployed name shown in the dashboard.

Only the allowed origin reaches your app. **404 is expected** at `pages.dev` and preview URLs. Preview and local environments have no production Service binding. For a test environment, explicitly configure a test Worker and a separate allowed origin. This setup does not disable the original Worker's public URL.

### 3. Deploy your Pages project and configure banchi DNS

Use your existing Wrangler environment. For Nix projects, enter the repository's dev shell first. Run `wrangler whoami` and select the same account as the Worker; with multiple accounts, set the intended `CLOUDFLARE_ACCOUNT_ID`.

This example creates a **new Direct Upload project**, preserving existing Pages projects. Direct Upload cannot later switch to Git integration. If you prefer automatic Git deployments, select Git integration when creating the project, use root `pages-entry`, an empty build command, output `dist`, and production branch `main`. Commit and push both files to that branch. If the existing `.gitignore` excludes `dist/`, run `git add -f pages-entry/dist/_worker.js` at the repository root to track only this entry file, and commit it together with its configuration.

```sh
cd pages-entry
wrangler pages project create my-worker-entry --production-branch main
wrangler pages deploy ./dist --project-name my-worker-entry --branch main
```

1. Open the new Pages project's **Custom domains → Set up a custom domain** and register your rented hostname.
2. In banchi, choose **Connect to my own hosting → Cloudflare Pages**, then save the production CNAME target shown by Pages (normally `my-worker-entry.pages.dev`). Do not enter a URL or deployment-specific preview name.
3. Wait for the domain and certificate to become active, then open `https://your-name.banchi.app`. Do not remove the origin check to fix the intentional `pages.dev` 404.
4. Run banchi's diagnosis and the application checks below. An app whose root returns 401/403 for authentication may not receive banchi's connected status even when HTTPS works.

### 4. Check the application and costs

- **URLs and authentication:** update the application's PUBLIC_ORIGIN equivalent, OAuth callbacks and Turnstile allowed hosts. Test sign-in, sign-out, forms and APIs; verify that signed-out visitors cannot access administrative functions. Use host-only cookies, preferably `__Host-`, without a parent `banchi.app` Domain attribute. Sibling addresses can be same-site, so validate the exact Origin for writes.
- **Assets and transport:** check JavaScript, CSS, images, deep links, 404s, Range and any WebSocket/SSE features. An explicit `env.ASSETS.fetch(request)` in the Worker and assets-first routing may differ. Test the application's actual dispatch path instead of copying its files or bindings into the entry or rewriting Request/Response objects.
- **Costs:** no additional fixed subscription is inherently required. Free Pages Functions and Workers share the account's 100,000 daily request allowance; requests fail after the limit. On existing Paid Standard, Service bindings have no additional request charge: the entry request and combined CPU of the entry and Worker count. Assets also invoke the entry Function, unlike free static-only delivery. Usage within your existing allowances adds no charge; overages, databases and other resources follow your hosting contract. banchi separately charges JPY 500 per address per year.

[Evidence](#evidence) covers the sample configuration, not every application, framework or external Access setup. To retire the entry, remove or change its target in banchi first, verify DNS propagation, then remove the Pages custom domain and entry project. Keep the existing Worker, databases and scheduled jobs.

### R2

Standard R2 Custom Domains also require an account-owned zone. If your existing Worker already serves R2 securely, this entry can call that HTTP functionality. Adding an R2 binding alone does not implement authorization, Range or caching. Direct R2 Custom Domain setup is outside this procedure.

Sources: [Workers Custom Domains](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/), [Pages custom domains](https://developers.cloudflare.com/pages/configuration/custom-domains/), [Service bindings and Access limitations](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/), [Pages configuration](https://developers.cloudflare.com/pages/functions/wrangler-configuration/), [Advanced mode](https://developers.cloudflare.com/pages/functions/advanced-mode/), [Pages CLI](https://developers.cloudflare.com/workers/wrangler/commands/pages/), [Direct Upload](https://developers.cloudflare.com/pages/get-started/direct-upload/), [Pages pricing](https://developers.cloudflare.com/pages/functions/pricing/), [Workers pricing](https://developers.cloudflare.com/workers/platform/pricing/), [R2 public buckets](https://developers.cloudflare.com/r2/buckets/public-buckets/).

<a id="checks"></a>
## 9. DNS and HTTPS verification commands

These checks do not change settings. Query only your own hostname and avoid turning them into repeated automated polling. Use `dig` and `curl` from your existing environment. With Nix, prefer your project's dev shell; if needed, `nix shell nixpkgs#dig nixpkgs#curl` supplies temporary tools.

```sh
BANCHI_HOST='your-name.banchi.app'
dig @1.1.1.1 "$BANCHI_HOST" A +short
dig @8.8.8.8 "$BANCHI_HOST" A +short
dig @1.1.1.1 "$BANCHI_HOST" AAAA +short
dig @8.8.8.8 "$BANCHI_HOST" AAAA +short
dig @1.1.1.1 "$BANCHI_HOST" CNAME +short
dig @8.8.8.8 "$BANCHI_HOST" CNAME +short
```

Compare the configured record type on both resolvers. A queries can include a CNAME chain and provider addresses; for a CNAME setup compare the CNAME target. An empty answer for a type you did not configure is not itself a failure. For TXT, use the complete challenge name supplied by the provider:

```sh
BANCHI_VERIFY_NAME='_acme-challenge.your-name.banchi.app'
dig @1.1.1.1 "$BANCHI_VERIFY_NAME" TXT +short
dig @8.8.8.8 "$BANCHI_VERIFY_NAME" TXT +short
```

Stale answers shortly after saving can be cached. Wait according to provider instructions instead of repeatedly deleting and recreating records. There is no universal 30-minute maximum for DNS and certificates.

```sh
curl --head --connect-timeout 5 --max-time 15 "https://$BANCHI_HOST/"
```

Only if HEAD returns 405 or 501, retry GET. This discards the body and does not follow redirects:

```sh
curl --silent --show-error --connect-timeout 5 --max-time 15 \
  --dump-header - --output /dev/null "https://$BANCHI_HOST/"
```

Do not disable certificate verification with `-k`. A 2xx/3xx proves a response, not working application features. Inspect a redirect's `Location`. A DNS-only HTTPS redirect still requires a valid certificate at the initial host.

| banchi code | What was checked | Next step |
| --- | --- | --- |
| `DNS_SAVED` | Records saved; no user-supplied root target | Check individual DNS records and child-host HTTPS |
| `HTTPS_REACHED` | Root HTTPS responded with a status such as 401/403/404/405 | Check authentication, path and hosting configuration in a browser |
| `LIVE` | Root DNS matches through two resolvers; HTTPS returns 200–399. HEAD 405/501 is retried with GET | Check redirects and real application operations; diagnosis does not follow redirects |

Diagnosis covers only the root. Check each additional child hostname separately. A detected provider error page can prevent `LIVE` even with a 200 response.

<a id="app-check"></a>
## 10. Authentication, forms and payments

These are settings for **your application** at the rented address, not banchi's own login or address-billing integration.

| Area | Update | Test |
| --- | --- | --- |
| Pages/API | Public base URL, canonical and asset/API URLs; use your app's actual variable names | Open and reload deep links; check old URLs, mixed content and 404s |
| OAuth | Allowed origins, callback and logout URLs, retaining required paths | Login, reload, logout and login again |
| Email login links | Public URL and return target generated by the app | Test-user links return to the new host |
| Forms/external API | Allow only required exact origins; retain CSRF validation | Submit, verify persistence, then reload |
| Turnstile | Add your rented hostname to your widget, without scheme/path/port | Complete the challenge and submit at the new host |
| Cookies | Prefer no Domain; where applicable use `__Host-` plus `Secure; Path=/` | Session persists on the new host and logout invalidates it |
| Stripe or other payments | Application success/cancel/return URLs; webhook endpoint if it moves | In a test environment: success, cancel, return, signature verification and duplicate-event handling |
| WebSockets/files | Proxy, authorization, Content-Type and Range behavior | Connect/reconnect, playback and required partial reads |

Sibling customer hosts may be considered the same site by browsers. Do not rely on SameSite alone for CSRF prevention. Validate origins and do not set parent-domain cookies. Do not solve authenticated CORS by using `*`, or allow the whole parent `banchi.app` in Turnstile.

Check webhook signing secrets separately for each endpoint and test/live environment; keep them server-side. Loading a success page alone does not prove payment fulfillment. Use the payment provider's test environment and test data, without creating unauthorized real charges.

Sources: [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS), [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie), [Turnstile hostnames](https://developers.cloudflare.com/turnstile/additional-configuration/hostname-management/), [Stripe Checkout URLs](https://docs.stripe.com/api/checkout/sessions/create), [Portal return URL](https://docs.stripe.com/api/customer_portal/sessions/create), [webhooks](https://docs.stripe.com/webhooks).

<a id="api"></a>
## 11. Existing API contract: the owner's browser session

This documents the current management UI API. There are no customer API keys, PATs, OAuth client credentials or DNS-update tokens. Do not integrate it as an unattended DNS-01 renewal API. Use only the actual management origin.

### Public reads

```sh
BANCHI_API='https://banchi.app'
curl --fail --silent --show-error --connect-timeout 5 --max-time 15 \
  "$BANCHI_API/api/config"
curl --fail --silent --show-error --connect-timeout 5 --max-time 15 \
  --get --data-urlencode 'q=your-name' --data-urlencode 'parent=banchi.app' \
  "$BANCHI_API/api/search"
```

`config` returns `price_jpy`, `parents`, `app_host` and other fields. A signed-in browser response can also contain the owner's email: do not publish full responses. `search` returns `{label, results:[{hostname,parent,status,reason}]}`, where `status` is `available`, `taken` or `invalid`. Availability is not a reservation or purchase guarantee.

### Owner operations

Sign in through the UI's email link and required human verification. Authentication uses the Secure, HttpOnly `__Host-sid` cookie, and the server checks ownership. Do not export cookies, login links, payment sessions or secrets to AI prompts or shared logs. Writes require a matching origin and are not supported from arbitrary external sites.

| Method / path | Purpose |
| --- | --- |
| `GET /api/labels` | Owner's address list, `{labels}` |
| `GET /api/labels/:hostname` | `{label, records, subscription}`, including `label.plan_version` |
| `PUT /api/labels/:hostname/records` | Save the complete desired record set; not an incremental add API |
| `POST /api/labels/:hostname/records/retry` | Reapply the saved plan; not an endpoint for new settings |
| `GET /api/labels/:hostname/diagnose` | Run actual checks; may finalize connection state on success, so not purely read-only |
| `PATCH /api/labels/:hostname` | Switch park/redirect/connect; switching to park or redirect removes connection DNS and verification TXT |

This read example runs only **inside the management origin in the owner's signed-in browser**. It does not extract the cookie. An assistant should use an existing authenticated UI rather than bypass authentication.

```js
const hostname = 'your-name.banchi.app'; // Replace with your own address.
const response = await fetch(`/api/labels/${encodeURIComponent(hostname)}`, {
  credentials: 'same-origin',
  headers: { Accept: 'application/json' },
});
if (!response.ok) throw new Error(`Read failed: HTTP ${response.status}`);
const detail = await response.json();
// Inspect locally; do not share full responses that may contain personal data.
```

### Individual record writes

PUT accepts `{ "plan_version": <latest integer>, "records": [ ... ] }`. Each record has `{ "id"?: <existing ID>, "type": "A"|"AAAA"|"CNAME"|"TXT"|"CAA", "name": <@ or complete name>, "content": <string> }`. This describes the shape; it is not a ready-to-send payload.

1. GET the detail immediately before editing; read `label.plan_version` and all records.
2. Exclude `managed_by: "system"` and `state: "deleting"` rows, then include every user record you intend to retain. Preserve existing IDs; omit IDs for new rows. Submit only `id/type/name/content` fields per row.
3. Review deletions and changes with the user. **Omitted existing records are deleted.** Do not send one TXT alone when you mean to add it to existing records.
4. Submit the full plan with the version from that read and a matching origin. Do not mix it with the simple `values` format. The individual format defaults to `generic` when `preset` is omitted.
5. Check returned record states and diagnosis. HTTP 200 does not mean global DNS propagation or working HTTPS; application may still be pending or awaiting retry.

On 409, reread and merge other changes with the user's intent; never merely substitute a new version into an old plan. On 401 sign in again; 403 can indicate origin rejection; 404 can mean missing or unowned; 429 requires waiting. After 5xx or timeout, first read the current state because a change may already have been applied. Do not rapidly replay mutations. Inspect `{error:{code,message}}` for the specific error.

Use the address-specific UI for purchase, payment and renewal cancellation. This manual does not authorize purchases or contract changes through the API.

<a id="retire"></a>
## 12. Migration and removal

Register the hostname at a new provider before changing banchi DNS. Keep the old deployment until DNS and application cutover are verified.

When ending use, first switch to park in banchi and verify removal/propagation of connection DNS and ownership TXT. Then remove the hosting custom domain, authentication/Turnstile allowed URLs and GitHub Verified domains entry. Deleting the host first while leaving DNS can allow another party to claim it.

Renewal cancellation is separate from disconnecting. Use the target address's cancellation button and confirm the contract in Stripe. Paid-period treatment follows the [terms](https://banchi.app/terms). If you cannot edit or confirm removal, use the [report form](https://banchi.app/help) with hostname, provider, time and diagnostic code. Do not include passwords, cookies, email login links or secrets.

<a id="evidence"></a>
## 13. Evidence

| Target | Observed | Date |
| --- | --- | --- |
| GitHub Pages | Normal acquisition/DNS UI, ownership verification, enforced HTTPS; Tsunagicho search, saving, sharing and file export | 2026-09-06 |
| Cloudflare Pages | DNS-only CNAME and HTTPS across accounts; test connection removed afterward | 2026-09-05 |
| Workers + Pages entry | Real DNS managed in another account, certificate, Service binding, URL/Host/client IP, Cookie/Origin/body, CSRF rejection, redirects, SSE/WebSocket, assets and conditional ETag; preview rejection | 2026-09-10 |
| Vercel / Netlify | Actual hosting connection, DNS diagnosis, certificate issuance and removal | 2026-09-07 |
| Public IPv4 VM and Caddy | GCE e2-micro, HTTP-01 issuance, DNS diagnosis, simple HTTPS response and removal | 2026-09-07 |

The Workers entry test used synthetic data and was separate from banchi purchase/DNS-save UI tests. Browser protection blocked the test hostname, so browser cookie persistence and real sign-in remain unverified. Static Assets returned the whole file with 200 for Range requests rather than 206; conditional ETag returned 304. Check partial-response requirements such as video seeking or resumable downloads separately. Existing application OAuth, Access, real payments and large/long-running traffic were outside this test.

Not demonstrated: end-to-end reachable IPv6 HTTPS, dual A/AAAA, manual DNS-01 issuance/renewal, nginx/Certbot, Caddy renewal at real expiry, or migration of an arbitrary Worker/R2 app to Pages. Official instructions and configuration examples do not constitute successful live tests of those paths.

The six additional services below—Amplify Hosting, Firebase Hosting, Azure Static Web Apps, Heroku, DigitalOcean App Platform and Cloud Run—have not had end-to-end banchi DNS, certificate and application tests. Direct CNAME/cross-account Custom Domain setup for Workers is not provided.

If provider specifications change, compare the current dashboard with the official links. Contact support rather than assuming support for records outside your rented namespace, nameserver migration or disclosure of secrets.

<a id="additional-providers"></a>
## 14. Additional hosts: instructions checked against official specifications

The selection follows the ten leading hosting-experience responses in [State of JavaScript 2025](https://2025.stateofjs.com/en-US/other-tools/) (10,704 respondents, multiple selections): AWS, Vercel, GitHub Pages, Netlify, Cloudflare, Firebase, Azure, Heroku, Digital Ocean and Google Cloud. We also checked [Stack Overflow 2025](https://survey.stackoverflow.co/2025/technology). This is not worldwide market share among solo developers, and a broad provider's count does not measure the particular service selected below. Cloudflare has two paths: [Pages](#cloudflare-pages) and [the Workers entry](#workers-r2).

These are **official-documentation-based instructions, without live connection tests**. Hosting charges are separate from banchi's price. Do not silently enable billing or broaden public access while following them. If parent-domain verification, nameserver changes or more than five records are mandatory, [contact support](https://banchi.app/help) rather than inventing replacement DNS values.

| Provider | Short visual guide | Details |
| --- | --- | --- |
| AWS Amplify Hosting | [Visual guide](https://banchi.app/guide-en#amplify) | [Instructions](#amplify) |
| Firebase Hosting | [Visual guide](https://banchi.app/guide-en#firebase) | [Instructions](#firebase) |
| Azure Static Web Apps | [Visual guide](https://banchi.app/guide-en#azure) | [Instructions](#azure) |
| Heroku | [Visual guide](https://banchi.app/guide-en#heroku) | [Instructions](#heroku) |
| DigitalOcean App Platform | [Visual guide](https://banchi.app/guide-en#digitalocean) | [Instructions](#digitalocean) |
| Google Cloud Run | [Visual guide](https://banchi.app/guide-en#google-cloud) | [Instructions](#google-cloud) |

<a id="amplify"></a>
### 14.1 AWS Amplify Hosting

**App preparation:** Select the repository and branch. Under **Hosting → Build settings**, check the build command and artifact `baseDirectory`. SSR needs a compatible framework setup or Amplify adapter; an arbitrary server start command is not sufficient. See [build settings](https://docs.aws.amazon.com/amplify/latest/userguide/build-settings.html) and [SSR adapters](https://docs.aws.amazon.com/amplify/latest/userguide/using-framework-adapter.html).

**Conditional; no live test.** Builds, storage and delivery incur usage charges. New AWS account credits are conditional and expire; do not present them as perpetual free hosting.

1. Deploy the existing app to Amplify and test its default URL, APIs and sign-in.
2. Open **Hosting → Custom domains → Add domain** and select **Manual configuration** for only your rented hostname. Review default root/www entries and redirects. Do not register the shared parent `banchi.app` or migrate nameservers.
3. In **View DNS records**, inspect the complete routing and certificate-verification CNAME names. Every name must be your rented hostname or below it. A generated name such as `_<issued-value>.your-name.banchi.app` fits the DNS editor, but never rewrite an actual parent-level requirement into this example.
4. Save the root and verification CNAMEs using banchi's individual editor, preserving the full intended record set. Stop if parent verification, ALIAS or NS changes are required. Avoid other records at the same name as a CNAME.
5. Wait for Amplify's domain and certificate to become usable, then run [external checks](#checks) and [application tests](#app-check). Keep the verification CNAME for renewal.

Amplify manages TLS. You configure and redeploy application URLs and authentication callbacks. Supporting the DNS record types alone does not prove every Amplify configuration works.

Sources: [External DNS setup](https://docs.aws.amazon.com/amplify/latest/userguide/to-add-a-custom-domain-managed-by-a-third-party-dns-provider.html), [pricing](https://aws.amazon.com/amplify/pricing/).

<a id="firebase"></a>
### 14.2 Firebase Hosting

**App preparation:** Follow the [Hosting quickstart](https://firebase.google.com/docs/hosting/quickstart), select the intended project, and point `hosting.public` in `firebase.json` to the built static output before deploying Hosting. Do not overwrite existing configuration or HTML during initialization or include secrets in public output. Static Hosting alone does not run an existing Node or other API; review [Cloud Run integration](#google-cloud) and its separate billing if needed.

**Conditional; no live test.** A required verification TXT at the parent `banchi.app` cannot be fulfilled here. Firebase Hosting and App Hosting are different products. Hosting Spark includes 10GB storage and 360MB/day transfer; dynamic integrations such as Cloud Run have Cloud Billing and Blaze requirements.

1. Deploy **Hosting** in the intended Firebase project and test its default URL. Review existing billing and usage; do not newly enable billing for a test intended to remain free.
2. Select **Add custom domain** and enter your rented hostname. Inspect any ownership-verification name. The official wizard can request apex verification, so confirm whether this particular setup can verify only your rented namespace.
3. For an allowed TXT name, save it in banchi and press **Verify**. Then save the A or other records supplied by **Quick Setup** or the selected flow. Retain verification TXT and remove conflicting old CNAMEs from the plan. Follow the current wizard if its record requirements differ.
4. Wait for **Connected** and a working certificate. Retain required TXT for ownership and renewal. If you configured CAA, check that it permits Firebase's certificate authorities.
5. Test pages, APIs and sign-in at the rented address. Check Firebase Authentication's allowed domains and OAuth callbacks where applicable.

Ownership verification depends on the configuration. The current REST model also describes verification through a TXT at the domain or a CNAME to the default Hosting domain; this does not authorize ignoring required parent verification. Shared-apex certificate and subdomain limits can also apply.

Sources: [Domain setup](https://firebase.google.com/docs/hosting/custom-domain), [ownership model](https://firebase.google.com/docs/reference/hosting/rest/v1beta1/projects.sites.customDomains), [pricing](https://firebase.google.com/pricing).

<a id="azure"></a>
### 14.3 Azure Static Web Apps

**App preparation:** Select the repository and branch. Configure `app_location` for app source, `api_location` for optional managed Functions source, and `output_location` for build output using the [build reference](https://learn.microsoft.com/en-us/azure/static-web-apps/build-configuration). This is not an arbitrary persistent server runtime. Integrated APIs use `/api`, have a **45-second maximum and no WebSocket support**. Managed Functions work on Free; Bring your own API requires Standard. See [API requirements](https://learn.microsoft.com/en-us/azure/static-web-apps/apis-overview).

**Official documentation checked; no live test.** Free supports two custom domains and automatic TLS for personal projects, without an SLA. Custom authentication registrations or connections to existing Functions may require Standard.

1. Test the existing Static Web App at its `azurestaticapps.net` URL. Use the Production environment, not a Preview domain configuration.
2. Save that app's default hostname as the root CNAME in banchi's individual DNS editor. Omit scheme and path.
3. In Azure, select **Settings → Custom domains → Add → Custom domain on other DNS**, enter the rented hostname and validate using **CNAME**. If TXT validation is required instead, confirm the complete name is within your rented namespace before saving it.
4. Once validation and automatic TLS work, test pages, managed Functions and sign-in at your rented address. Check plan requirements and callbacks for custom authentication providers.

Other configurations, including enterprise-grade edge, require TXT validation; do not apply CNAME-only instructions universally. This external-DNS path does not require creating an Azure DNS zone or migrating nameservers.

Sources: [External DNS](https://learn.microsoft.com/en-us/azure/static-web-apps/custom-domain-external), [domain conditions](https://learn.microsoft.com/en-us/azure/static-web-apps/custom-domain), [plans](https://learn.microsoft.com/en-us/azure/static-web-apps/plans).

<a id="heroku"></a>
### 14.4 Heroku

**App preparation:** Define the `web` process start command, for example in a `Procfile`, and listen on the `PORT` environment variable supplied by Heroku. Local files and memory are lost on restarts; persist data in a separate database or storage service. Test startup and persistence on the default URL before adding a domain. See the [runtime contract](https://devcenter.heroku.com/articles/runtime-principles).

**Official documentation checked; no live test.** App compute is paid. Eco costs US$5/month and sleeps after 30 minutes of inactivity, among other limits. Check database and always-on compute costs separately.

1. Test your existing Heroku app's default URL and review account-verification and plan requirements for custom domains.
2. Add your rented hostname under **Settings → Domains and certificates**.
3. Save the unique supplied **DNS Target** ending in `herokudns.com` as banchi's root CNAME. Do not substitute the app's `herokuapp.com` URL or another app's target.
4. Enable **Automated Certificate Management (ACM)** and wait for a working certificate. Eco now supports ACM; do not follow outdated advice that requires your own certificate.
5. Update allowed Host/Origin and other application settings, redeploy if needed, then run [application checks](#app-check). Maintain the DNS and domain registration for ACM renewal.

Sources: [Custom domains](https://devcenter.heroku.com/articles/custom-domains), [ACM](https://devcenter.heroku.com/articles/automated-certificate-management), [pricing](https://www.heroku.com/pricing/).

<a id="digitalocean"></a>
### 14.5 DigitalOcean App Platform

**App preparation:** Choose **Static Site** for static files or a paid **Web Service** for a persistent API. Check the repository, branch, build command and static output directory. For a Web Service, match its run command and HTTP port to the app's listener, then confirm deployment health. Review the detected component type, size and cost before creating it. See [app creation](https://docs.digitalocean.com/products/app-platform/how-to/create-apps/) and [build/run commands](https://docs.digitalocean.com/products/app-platform/how-to/build-run-commands/).

**Conditional; no live test.** Official documentation says DNSSEC-enabled domains are unsupported. Check current conditions before purchasing. The free tier for up to three static-only apps has transfer limits; dynamic services and databases cost extra.

1. Test the existing App Platform app at its default URL. If you cannot establish whether its DNSSEC condition is met, ask before adding the domain. Do not change the shared parent's DNSSEC to bypass this restriction.
2. Open **Networking → Domains → Add domain**, enter your rented hostname and choose **You manage your domain**. Do not choose the **We manage your domain** nameserver path.
3. Save the supplied `ondigitalocean.app` CNAME at the root in banchi, then complete domain addition in DigitalOcean. If an A-record path is needed, use only the values and conditions supplied for that app.
4. Wait for a working domain and certificate, then test the root page, APIs and authentication at the rented hostname.

The static free tier includes 1GiB transfer per app; excess transfer is charged. Saving a CNAME in banchi does not prove App Platform has accepted the domain or issued its certificate.

Sources: [Domain conditions and setup](https://docs.digitalocean.com/products/app-platform/how-to/manage-domains/), [pricing](https://docs.digitalocean.com/products/app-platform/details/pricing/).

<a id="google-cloud"></a>
### 14.6 Google Cloud Run through Firebase Hosting

**App preparation:** Use an HTTP Cloud Run **service**. Its ingress container must listen on `0.0.0.0`, not `127.0.0.1`, using the injected `PORT` (default 8080). Cloud Run terminates TLS; do not configure a certificate on the container listener. See the [container contract](https://docs.cloud.google.com/run/docs/container-contract). Review exposure, IAM and ingress; test the default URL without making a private app unintentionally public.

**Conditional; no live test.** Direct Cloud Run Domain mapping requires parent-domain ownership, which normal customers cannot prove for `banchi.app`. It is also a Preview feature not recommended for production. The official Hosting path below requires Cloud Billing and Blaze. **Requests over 60 seconds and existing authentication needing cookies other than `__session` cannot pass through unchanged.**

1. Test the existing Cloud Run URL. Review Hosting, Cloud Billing and Blaze requirements within the same Firebase / Google Cloud project. If billing must be newly enabled, stop until the owner has decided.
2. Confirm the app can work within Hosting's timeout and cookie restrictions. A public HTTP service still needs application-level authentication where appropriate. Do not remove existing IAM protection simply to fit this example.
3. Add the intended path and Cloud Run `serviceId` and `region` to `hosting.rewrites` in the existing `firebase.json`. This example routes only `/api/**`. Merge it rather than replacing the entire configuration; use your real service, region and paths.

```json
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "/api/**",
        "run": {
          "serviceId": "your-cloud-run-service",
          "region": "us-central1"
        }
      }
    ]
  }
}
```

4. Verify the selected project and public output, then deploy only Hosting through your existing process. With an already installed, authenticated Firebase CLI and the correct project selected, the official command is `firebase deploy --only hosting`. Run it inside your existing Nix environment when applicable. **It changes the public deployment.** Test static files, rewritten paths and sign-in on the default `web.app` or equivalent URL first.
5. Follow [Firebase Hosting setup](#firebase) to add your rented hostname and save the required DNS within its namespace. Stop if parent TXT verification is requested. Once HTTPS works, run [application checks](#app-check).

`public` is an example static output directory. Static files can take precedence over rewrites, so review conflicting output paths and existing rules. Do not weaken authentication merely by renaming a cookie; retain host-limited cookies and strict application validation. This does not introduce a new banchi API or customer token.

Google's recommended alternative is an external HTTPS load balancer with a serverless NEG, with banchi A/AAAA records pointing to its frontend. It adds separate configuration and charges for the load balancer, IP and traffic. Discuss costs before buying or creating it; Cloud Run's free allowance does not make the entire configuration free.

Sources: [Hosting and Cloud Run](https://firebase.google.com/docs/hosting/cloud-run), [cookie and cache behavior](https://firebase.google.com/docs/hosting/manage-cache#using_cookies), [rewrite priority](https://firebase.google.com/docs/hosting/full-config#hosting_priority_order), [custom domain options](https://docs.cloud.google.com/run/docs/mapping-custom-domains), [network pricing](https://cloud.google.com/vpc/network-pricing).
