算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
) needs a collision-free encoding or a hash; replacing dots with underscores maps `a-b.example.com` and `a.b-example.com` to one key. Keep the original hostname in the record and compare it on read.\n- Starting path-based with custom domains on the roadmap means URL rewrites, cookie changes, and DNS migration later.\n\n## Output schema\n\nLength follows the decisions: drop any section the project does not face rather than filling it.\n\n```markdown\n# Multi-tenant architecture\n\n## Platform decision\n- Platform: Cloudflare | Vercel\n- Why this platform:\n- Rejected platform and reason:\n\n## Domain map\n- Brand domain:\n- Tenant domain:\n- Tenant subdomains:\n- Custom domains:\n- PSL decision: Submit (suffix, owner, PR link, _psl TXT date) | No PSL (reason)\n\n## Routing matrix\n| Host pattern | Resolver | Destination | Unknown tenant behavior |\n|---|---|---|---|\n\n## Tenant context flow\n- Authority: proxy.ts | dispatch Worker\n- Headers set and stripped:\n- Server read path:\n- Data-layer enforcement:\n\n## Isolation model\n- Compute isolation:\n- Data isolation (and per-plan variant):\n- Config/binding isolation:\n\n## Custom-domain lifecycle\n1. DNS target:\n2. Ownership verification:\n3. Certificate provisioning:\n4. Routing activation:\n5. Removal/failure path:\n\n## Limits-to-plan table\n| Limit | Source URL / access date | Free | Pro | Enterprise | Enforcement point |\n|---|---|---:|---:|---:|---|\n\n## Validation evidence\n| Check | Command | Expected | Result |\n|---|---|---|---|\n```\n\n## Evidence commands\n\nRun against local or preview before calling the design done; mark N/A with a reason.\n\n| Check | Command | Expected |\n|---|---|---|\n| Tenant boundary exists in code | `rg -n \"x-tenant-id\\|CREATE POLICY\\|FORCE ROW LEVEL SECURITY\\|DISPATCHER.get\" .` | Hits in proxy or dispatch Worker and in the schema |\n| Unknown host is 404 | `curl -sI -H \"Host: nope.acme.app\" \u003curl>` | `404` |\n| Forged header ignored | `curl -s -H \"Host: a.acme.app\" -H \"x-tenant-id: tenant-b\" \u003curl>/api/whoami` | Tenant A |\n| Static files vary | `curl -s -H \"Host: a.acme.app\" \u003curl>/robots.txt` vs `-H \"Host: b.acme.app\"` | Different bodies, `Content-Type: text/plain` |\n| RLS holds for the app role | `psql -c \"BEGIN; SET LOCAL ROLE app_user; SELECT set_config('app.tenant_id','\u003ct1>',true); SELECT count(*) FROM posts; ROLLBACK;\"` | Only tenant t1's rows |\n| ACME path reachable | `curl -sI -H \"Host: tenant.com\" \u003curl>/.well-known/acme-challenge/test` | Not a redirect into the tenant segment |\n| Limits current | Access date next to each URL in the limits table | Dated within the planning window |\n\n## Related skills\n\n- `codebase-architecture`: folder structure, module contracts, and the request-context pipeline for the application itself.\n- `scaffold-nextjs`: bootstrap the Next.js turborepo before applying these tenancy patterns.\n- `seo`: content of per-tenant `robots.txt`, `sitemap.xml`, `llms.txt`, canonical URLs, and structured data once routing serves them.\n\nMaintenance only: `evals/evals.json` contains regression scenarios for changes to this skill; it does not load during a user task.\n","licenseDocuments":[]}}
multi-tenant-architecture【设计与创意】Designs tenant isolation, hostname routing, custom-domain lifecycle, and plan limits on Cloudflare or Vercel. Use when asked to "isolate tenant data", "support custom domains", "build a white-label platform", or assess PSL registration. For general module structure use codebase-architecture; for SEO content use seo.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
codebase-architecture), scaffolding a new repo (use scaffold-nextjs), or the content of per-tenant SEO files once routing serves them dynamically: sitemap entries, canonical URLs, structured data, indexing policy (use seo).| Signals | Platform | Model |
|---|---|---|
| Tenants upload or generate their own code; code-level isolation; edge compute on KV, D1, Durable Objects, R2 | Cloudflare | Dispatch Worker in front of a dispatch namespace of per-tenant Workers; Cloudflare for SaaS for custom hostnames |
| Every tenant runs the same Next.js codebase and differs by content, branding, and plan; ISR, Server Components, Vercel deploys | Vercel | One deployment; proxy.ts resolves the tenant from the hostname; wildcard plus custom domains on the project |
| File | Read when |
|---|---|
| cloudflare-platform.md | Cloudflare chosen: dispatch namespaces, routing, Cloudflare for SaaS custom hostnames, isolation modes, KV and D1 (steps 3 to 7) |
| vercel-platform.md | Vercel chosen: proxy.ts resolution, App Router layout, Global Config lookups, per-tenant static files, custom subpaths, local dev (steps 4 to 7) |
| vercel-domains.md | Vercel chosen: SDK domain lifecycle, DNS targets, verification, wildcard nameservers, SSL, troubleshooting (step 7) |
| data-isolation.md | Step 3 on either platform: shared schema with RLS, schema-per-tenant, database-per-tenant, and the Postgres/Supabase/Drizzle policy pattern |
| psl.md | Step 1 when tenants publish content or run code on sibling subdomains: eligibility, submission, interim cookie controls |
| limits-and-quotas.md | Step 8: Cloudflare, Vercel, and Neon limit names with the official source for each, to map onto plans |
Copy this checklist to track progress:
Multi-tenant progress:
- [ ] Step 1: Domain strategy and PSL decision
- [ ] Step 2: Tenant identification strategy
- [ ] Step 3: Isolation model (compute and data)
- [ ] Step 4: Deterministic routing
- [ ] Step 5: Tenant context propagation
- [ ] Step 6: Tenant config and least-privilege bindings
- [ ] Step 7: Custom domains and per-tenant static files
- [ ] Step 8: Limits mapped to plans, evidence captured
acme.app for tenants, acme.com for brand). One phishing tenant on x.acme.com puts the whole domain on blocklists, and a tenant cookie with Domain=acme.com reaches your dashboard.app.acme.com for the console, *.acme.app for tenants).acme.app, or sites.acme.app for <tenant>.sites.acme.app) to the PSL and start now: there is no SLA. Tenant-owned custom domains need no PSL entry. Otherwise record No PSL with the reason.tenant.acme.app: wildcard DNS plus wildcard certificate. The default.tenant.com: the tenant CNAMEs to you. Paying tenants; reputation shifts to them; needs the onboarding lifecycle in step 7.acme.app/tenant: no per-tenant DNS or certificates, but no cookie isolation and no branding. Choose it only when tenants will never get a hostname.cpuMs and subRequests limits per plan; an outbound Worker if tenant code may call the internet.tenant_id on every tenant-aware table plus RLS is the default; database-per-tenant for regulated or noisy tenants, selectable per plan. See data-isolation.md.*/* route on the SaaS zone to the dispatch Worker; hostname -> tenant record (KV, D1 on miss) -> env.DISPATCHER.get(script); Worker not found -> 404.proxy.ts (Next.js 16; middleware.ts with runtime: 'nodejs' on 15) reads host, looks the tenant up in Global Config or the database, rewrites into the tenant segment; unknown hostname -> 404, never the brand site./.well-known through before any tenant rewrite. Route robots.txt, sitemap.xml, and llms.txt into the tenant segment so they vary per tenant.x-tenant-* header, set x-tenant-id, x-tenant-slug, x-tenant-plan from the resolved tenant, and forward them on the request (NextResponse.next({ request: { headers } })). Server Components read await headers(); route handlers read request.headers. Cloudflare: the dispatch Worker sets headers or passes parameters before fetch.tenant_id predicates).hostname -> { id, slug, plan }; the database is the source of truth and write-through happens when a domain verifies. Feature flags and branding come from the database keyed by tenant id.customers.<you>.com CNAME target, http or txt validation, pre-validate before DNS cutover. See cloudflare-platform.md.projectsAddProjectDomain -> DNS values from the project's domain card -> _vercel TXT only if the domain is already on Vercel -> projectsVerifyProjectDomain -> Let's Encrypt HTTP-01. See vercel-domains.md.robots.txt, sitemap.xml, llms.txt are route handlers inside the tenant segment with explicit Content-Type; nothing tenant-specific lives in /public. Their content is seo territory.limits, Vercel plan header plus server checks).Platform and data gotchas live in the reference for that step; these fire before any reference loads.
curl -H "x-tenant-id: <other>" then serves another tenant's data. Delete or overwrite x-tenant-* on every path through the proxy or dispatch Worker, including paths that skip resolution (/.well-known, the brand site).^[\w-]+$) needs a collision-free encoding or a hash; replacing dots with underscores maps a-b.example.com and a.b-example.com to one key. Keep the original hostname in the record and compare it on read.Length follows the decisions: drop any section the project does not face rather than filling it.
# Multi-tenant architecture
## Platform decision
- Platform: Cloudflare | Vercel
- Why this platform:
- Rejected platform and reason:
## Domain map
- Brand domain:
- Tenant domain:
- Tenant subdomains:
- Custom domains:
- PSL decision: Submit (suffix, owner, PR link, _psl TXT date) | No PSL (reason)
## Routing matrix
| Host pattern | Resolver | Destination | Unknown tenant behavior |
|---|---|---|---|
## Tenant context flow
- Authority: proxy.ts | dispatch Worker
- Headers set and stripped:
- Server read path:
- Data-layer enforcement:
## Isolation model
- Compute isolation:
- Data isolation (and per-plan variant):
- Config/binding isolation:
## Custom-domain lifecycle
1. DNS target:
2. Ownership verification:
3. Certificate provisioning:
4. Routing activation:
5. Removal/failure path:
## Limits-to-plan table
| Limit | Source URL / access date | Free | Pro | Enterprise | Enforcement point |
|---|---|---:|---:|---:|---|
## Validation evidence
| Check | Command | Expected | Result |
|---|---|---|---|
Run against local or preview before calling the design done; mark N/A with a reason.
| Check | Command | Expected |
|---|---|---|
| Tenant boundary exists in code | rg -n "x-tenant-id|CREATE POLICY|FORCE ROW LEVEL SECURITY|DISPATCHER.get" . | Hits in proxy or dispatch Worker and in the schema |
| Unknown host is 404 | curl -sI -H "Host: nope.acme.app" <url> | 404 |
| Forged header ignored | curl -s -H "Host: a.acme.app" -H "x-tenant-id: tenant-b" <url>/api/whoami | Tenant A |
| Static files vary | curl -s -H "Host: a.acme.app" <url>/robots.txt vs -H "Host: b.acme.app" | Different bodies, Content-Type: text/plain |
| RLS holds for the app role | psql -c "BEGIN; SET LOCAL ROLE app_user; SELECT set_config('app.tenant_id','<t1>',true); SELECT count(*) FROM posts; ROLLBACK;" | Only tenant t1's rows |
| ACME path reachable | curl -sI -H "Host: tenant.com" <url>/.well-known/acme-challenge/test | Not a redirect into the tenant segment |
| Limits current | Access date next to each URL in the limits table | Dated within the planning window |
codebase-architecture: folder structure, module contracts, and the request-context pipeline for the application itself.scaffold-nextjs: bootstrap the Next.js turborepo before applying these tenancy patterns.seo: content of per-tenant robots.txt, sitemap.xml, llms.txt, canonical URLs, and structured data once routing serves them.Maintenance only: evals/evals.json contains regression scenarios for changes to this skill; it does not load during a user task.