gatekeeper-nginx-setup
Use this skill when the user asks to set up nginx in front of a ByteForge Gatekeeper deployment, configure the gatekeeper vhost, fix Cloudflare Error 1000 / `:3000` redirect leaks for a Gatekeeper site, or wire `auth_request` CORS for a service protected by Gatekeeper's `/authz`. Generates a drop-in nginx vhost (HTTP→HTTPS, `/api/*` → Flask backend, `/` → Next.js frontend) with the four traps documented inline so they don't get "simplified" away, plus optional `/authz` + `/health` + `/metrics` and an optional CORS-via-auth_request configuration for protected upstream services.
What this skill does
# Gatekeeper nginx vhost setup
This skill produces a complete nginx vhost for a ByteForge Gatekeeper deployment running behind Cloudflare:
- Flask backend container (gunicorn on port 7843)
- Next.js console frontend container (port 3000)
- Cloudflare Origin certificate already installed on the host
It also covers the optional bits that come up in real deployments — root-level `/authz`/`/health`/`/metrics` exposure, and CORS wiring when **another** vhost on this host (or another host) protects its upstream with `auth_request` against Gatekeeper's `/authz`.
The vhost shape, header set, and resolver trick are not arbitrary. Each load-bearing decision is documented in **Traps** below — read that section before editing anything the skill emits.
## When to Use This Skill
- Bringing up a new Cloudflare-fronted Gatekeeper deployment on a fresh host
- Migrating a Gatekeeper deployment to a new domain
- Debugging `307 → https://<DOMAIN>:3000/<locale>` redirect leaks (the i18n trap)
- Debugging Cloudflare Error 1000 (`DNS points to prohibited IP`) on `/api/*` calls (the catch-all trap)
- Adding `auth_request` CORS to a protected upstream service that delegates authz to a Gatekeeper instance
- Auditing an existing Gatekeeper vhost against the documented traps
## What This Skill Creates
1. **An nginx server block** — HTTP→HTTPS redirect, TLS 443 listener with Cloudflare Origin cert, `/api/*` proxy to backend, `/` proxy to frontend with the i18n-safe forwarded headers
2. **A `resolver` check + directive snippet** — needed because the vhost uses `set $var <container>; proxy_pass http://$var:port;` to defer DNS to request time
3. **(Optional) Root-level endpoints** — `/authz`, `/health`, `/metrics` if external systems need them through this vhost
4. **(Optional) `auth_request` + CORS configuration** — for a separate vhost that delegates authz to this Gatekeeper deployment
5. **Smoke-test commands** — four `curl`s that prove every code path works
6. **A failure-decoder table** — symptom → likely cause → fix, mapped to the trap numbers
## Step 1: Gather Information
Ask the user these questions before generating any config. Substitute the answers consistently throughout.
1. **"What is the public domain for this Gatekeeper deployment?"** (e.g., `gatekeeper.example.com`)
- Stored as `<DOMAIN>`
2. **"What is the Docker service name of the Flask/gunicorn backend container?"** (e.g., `gatekeeper-backend`)
- Stored as `<BACKEND_CONTAINER>`
- Must be reachable by hostname on the docker network nginx is attached to
3. **"What is the Docker service name of the Next.js frontend container?"** (e.g., `gatekeeper-frontend`)
- Stored as `<FRONTEND_CONTAINER>`
- Must be reachable by hostname on the docker network nginx is attached to
4. **"Where do the Cloudflare Origin certificate files live for this host?"** (default: `/etc/nginx/ssl/<DOMAIN>.crt` and `.key`)
- Adjust to the host's nginx convention if different
5. **"Is nginx running on the host directly, or as a container on the same docker network as the Gatekeeper containers?"**
- Determines which `resolver` IP to use:
- **Container nginx on a docker user network** → `127.0.0.11` (docker's embedded resolver)
- **Host nginx using systemd-resolved** → `127.0.0.53`
- **Host nginx using upstream DNS** → whatever the host actually uses
6. **"Do any external systems (load balancers, monitoring, sibling vhosts) need to reach `/authz`, `/health`, or `/metrics` on this domain?"**
- If **no**, skip Step 4
- If **yes**, ask which paths and why — `/authz` in particular often wants `internal;`
7. **"Are there any other services on this host (or another host) that delegate authorization to this Gatekeeper instance via `auth_request /authz`?"**
- If **yes**, walk Step 5 (CORS via auth_request)
- If **no** or unsure, skip Step 5
## Step 2: Verify a `resolver` directive exists
The vhost emitted in Step 3 uses `set $var <container>; proxy_pass http://$var:port;` deliberately, so nginx defers DNS resolution to **request time** instead of config-load time. That makes the vhost survive container restarts — but it requires a `resolver` directive somewhere included into the http block.
Run this on the host where nginx config lives:
```bash
grep -r "^[[:space:]]*resolver " /etc/nginx/ 2>/dev/null
```
(Adjust the path if nginx config lives in a docker volume or a non-standard location.)
- **A `resolver` line is present** → done, proceed to Step 3.
- **No `resolver` line** → add this once at the top of `nginx.conf`'s `http {}` block (or include a small file that does):
```nginx
# Container nginx on a docker user network — use docker's embedded resolver.
# valid= sets the cache TTL.
resolver 127.0.0.11 valid=10s ipv6=off;
# Host nginx with systemd-resolved:
# resolver 127.0.0.53 valid=10s ipv6=off;
```
Without this, the variable-form `proxy_pass` fails silently with "no resolver defined" and `502 Bad Gateway` at request time. This is **Trap 4** — do not skip it.
## Step 3: Generate the Drop-in vhost
Substitute `<DOMAIN>`, `<BACKEND_CONTAINER>`, `<FRONTEND_CONTAINER>` with the values from Step 1.
```nginx
# HTTP -> HTTPS redirect
server {
listen 80;
server_name <DOMAIN>;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name <DOMAIN>;
ssl_certificate /etc/nginx/ssl/<DOMAIN>.crt;
ssl_certificate_key /etc/nginx/ssl/<DOMAIN>.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
access_log /var/log/nginx/<DOMAIN>_access.log;
error_log /var/log/nginx/<DOMAIN>_error.log;
# All /api/* (admin, auth, config, webhooks/aegis, ...) goes to the
# gatekeeper backend. The frontend defines no /api/* routes — routing
# an unmapped /api/* to Next.js will server-side-fetch the same origin
# and loop through Cloudflare, producing Error 1000. See Trap 1.
location /api/ {
set $gk_backend <BACKEND_CONTAINER>;
proxy_pass http://$gk_backend:7843;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 30s, not 10s: the /api/auth/* proxy makes synchronous outbound
# calls to Aegis, which can occasionally take longer under cold
# start or upstream DB pressure. The gatekeeper itself responds
# in single-digit ms; the headroom is for Aegis.
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
location / {
set $gk_frontend <FRONTEND_CONTAINER>;
proxy_pass http://$gk_frontend:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# X-Forwarded-Host + X-Forwarded-Port are required: without them,
# Next.js 16's i18n middleware bakes the UPSTREAM port (3000) into
# the Location header on its locale redirect, and browsers then
# try to follow https://<DOMAIN>:3000/en, which fails. See Trap 2.
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
Drop this into the host's nginx layout — typically one of:
- `/etc/nginx/conf.d/<DOMAIN>.conf`
- `/etc/nginx/sites-available/<DOMAIN>` (then `ln -Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.