Documentation Feedback System
The StackConsole Documentation Feedback System provides an in-portal, privacy-first mechanism for cloud provider administrators to submit anonymous ratings ("Was this page helpful? ๐ Yes / ๐ No") and structured improvement proposals directly from any documentation page across https://admindoc.stackconsole.io/.
Admins do not need an external GitLab account, StackConsole login, or documentation credentials to submit feedback. Submissions are persisted into an isolated PostgreSQL database via a lightweight, secure Fastify backend microservice (stackconsole-docs-feedback).
1. Executive Summary & Purposeโ
Why the Feedback System Existsโ
Documentation quality directly impacts cloud administrator productivity and support ticket volume. The documentation feedback subsystem enables:
- Immediate Sentiment Capture: Instant binary feedback (Thumbs Up / Thumbs Down) on whether a guide resolved the administrator's technical question.
- Categorized Issue Reporting: Clear identification of documentation defects (e.g., outdated instructions, missing steps, broken links).
- Proactive Improvement Suggestions: Open-ended proposals from platform operators, including an optional email address for follow-up questions.
- Zero Friction: Completely anonymous submission model. Admins are never redirected to third-party issue trackers or forced to authenticate.
- Decoupled Architecture: Documentation browsing is 100% static HTML and remains fully functional even if the feedback API or database is offline.
Relationship to Admin Documentation and Docs AIโ
The feedback subsystem operates as an independent backend service alongside the documentation portal and the RAG-powered documentation assistant (stackconsole-docs-ai):
| Subsystem | Primary Role | Host Port | Database / Engine | Public Exposure |
|---|---|---|---|---|
| Admin Docs Portal | Static documentation site | 80 / 443 | Docusaurus v3 Static HTML | Public WAN |
| Docs Feedback Service | In-portal rating & proposal persistence | 8001 | PostgreSQL 16 Alpine | Reverse Proxy (/api/v1/feedback) |
| Docs AI Assistant | Grounded RAG query answering | 8000 | Qdrant Vector DB & Ollama | Reverse Proxy (/api/v1/ai/) |
2. System Architecture & Component Topologyโ
Component Breakdownโ
-
Frontend UI Components (
DocFeedback&FeedbackModal):- Location:
src/components/Feedback/DocFeedback.tsxandsrc/components/Feedback/FeedbackModal.tsx. - Injected globally into all documentation pages via Docusaurus layout swizzling (
src/theme/DocItem/Layout/index.tsx). - Zero markdown file modifications required.
- Dynamic version awareness: automatically detects active documentation version (e.g.,
2.0or upcoming3.0). - Accessibility compliant: supports full keyboard navigation, focus trapping in modal dialogs, and ESC key dismissal.
- Location:
-
Backend API Gateway (
stackconsole-docs-feedback):- Location:
stackconsole-docs-feedback/src/app.ts. - Node.js 20+ runtime with Fastify v5 web framework.
- Schema enforcement via Zod (
src/validation/schema.ts). - In-memory sliding-window rate limiter (
src/security/rate-limiter.ts) enforcing 30 requests/minute per client IP. - Hardened security headers via
@fastify/helmetand strict domain origin whitelisting via@fastify/cors. - Structured JSON logging (
src/observability/logger.ts) with mandatory zero-PII redaction. - Prometheus metrics exposition (
GET /metrics).
- Location:
-
Relational Persistence Layer (
stackconsole-feedback-db):- Image:
postgres:16-alpine. - Container Name:
stackconsole-feedback-db. - Network: Dedicated private Docker bridge
stackconsole-feedback-network. - Network Boundary: Port
5432is unmapped to the host interface. It is accessible exclusively bystackconsole-docs-feedbackthrough the internal bridge. - Storage Volume: Persistent named Docker volume
feedback_pgdatamounted at/var/lib/postgresql/data.
- Image:
-
Decoupled Notification Dispatcher (
FeedbackMailer):- Location:
stackconsole-docs-feedback/src/notifications/mailer.ts. - Controlled via
FEEDBACK_EMAIL_ENABLED(default:false). - Non-blocking design: email alerts fire asynchronously in the background. Database transactions commit independently; an SMTP timeout or refusal never fails a user's submission.
- Location:
3. Prerequisitesโ
Local Development Requirementsโ
- Operating System: macOS 12+, Ubuntu 20.04+, or Debian 11+
- Node.js:
v20.xor higher (node -v) - Package Manager:
npm v10.xor higher (npm -v) - Docker: Docker Engine
20.10.xor higher (docker -v) - Docker Compose: Compose plugin
v2.0.xor higher (docker compose version) - Git:
2.30+
Production Host Requirementsโ
- Host Specs: Minimum 1 vCPU, 1 GB RAM (Recommended: 2 vCPU, 2 GB RAM).
- Storage: Minimum 10 GB free disk space for persistent PostgreSQL volume and rotating backup dumps.
- Firewall & Ports:
- Port
443/80: Public ingress (Nginx / Cloudflare). - Port
8001: Bound to127.0.0.1:8001or reverse-proxied internally. - Port
5432: NEVER exposed publicly.
- Port
4. Installation & Local Developmentโ
Follow this step-by-step procedure to run the entire feedback subsystem locally.
Step 1: Clone Repository & Navigate to Microserviceโ
git clone https://gitlab.stackconsole.io/stackconsole/admin-docs.git
cd admin-docs/stackconsole-docs-feedback
Step 2: Install Dependenciesโ
npm ci
Step 3: Configure Environment Variablesโ
Copy the environment template and inspect default values:
cp .env.example .env
For local development against Docker Compose, the default .env settings connect the Fastify service to the PostgreSQL container.
Step 4: Run Automated Testsโ
Execute the unit and API test suite to verify code integrity:
npm test
Expected Result: 24 passing tests across schema validation, rate limiting, and API routes.
Execute static type checking:
npm run typecheck
Expected Result: Clean exit with zero TypeScript compilation errors.
Step 5: Start Subsystem via Docker Composeโ
From stackconsole-docs-feedback/:
docker compose up -d --build
Verify container states:
docker compose ps
Expected Output:
NAME COMMAND SERVICE STATUS PORTS
stackconsole-docs-feedback "docker-entrypoint.sโฆ" docs-feedback running (healthy) 0.0.0.0:8001->8001/tcp
stackconsole-feedback-db "docker-entrypoint.sโฆ" feedback-db running (healthy) 5432/tcp
Step 6: Verify Service Healthโ
Probe the liveness and readiness endpoints:
# Liveness probe
curl -s http://localhost:8001/health
# Readiness probe (verifies live database connection pool)
curl -s http://localhost:8001/ready
Expected Output:
{"status":"ok","service":"stackconsole-docs-feedback","uptime":12.5,"timestamp":"2026-10-03T09:00:00.000Z"}
{"status":"ready","service":"stackconsole-docs-feedback","database":"connected"}
Step 7: Submit a Test Feedback Payloadโ
Submit a positive rating:
curl -s -X POST http://localhost:8001/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "HELPFUL_POSITIVE",
"page_title": "Installation Guide",
"page_url": "http://localhost:3000/installation",
"page_path": "/installation",
"docs_version": "2.0"
}'
Expected Output:
{
"success": true,
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message": "Feedback recorded successfully. Thank you for helping improve StackConsole documentation."
}
Step 8: Inspect Database Persistenceโ
Query the PostgreSQL container directly:
docker exec -t stackconsole-feedback-db psql -U feedback_app -d stackconsole_feedback -c \
"SELECT id, feedback_type, page_title, docs_version, status, created_at FROM docs_feedback ORDER BY created_at DESC LIMIT 1;"
Step 9: Teardown Local Environmentโ
# Stop containers while preserving data volume
docker compose down
# To clean up completely including volume (DEVELOPMENT ONLY):
# docker compose down -v
5. Configuration Referenceโ
All settings are configured via environment variables supplied to the stackconsole-docs-feedback container or local process.
| Environment Variable | Required | Default Value | Description | Secret? | Production Recommendation |
|---|---|---|---|---|---|
NODE_ENV | Yes | production | Node runtime environment (production, development, test) | No | production |
APP_PORT | No | 8001 | TCP port for the Fastify feedback service | No | 8001 |
APP_HOST | No | 0.0.0.0 | IP bind address for Fastify | No | 0.0.0.0 |
POSTGRES_HOST | Yes | feedback-db | Database hostname or Docker service name | No | feedback-db (Docker) or private IP |
POSTGRES_PORT | No | 5432 | PostgreSQL listening port | No | 5432 |
POSTGRES_DB | Yes | stackconsole_feedback | PostgreSQL database name | No | stackconsole_feedback |
POSTGRES_USER | Yes | feedback_app | PostgreSQL application user | No | Dedicated unprivileged user |
POSTGRES_PASSWORD | Yes | (none) | Strong password for PostgreSQL user | YES | Strong random 32+ char secret |
POSTGRES_SSL | No | false | Enable TLS/SSL for database connection | No | false in Docker network; true if external |
POSTGRES_MAX_CONNECTIONS | No | 20 | Max client connections in pg pool | No | 20 to 50 |
DATABASE_URL | No | (none) | Complete connection string (overrides individual POSTGRES_* vars) | YES | Optional alternative |
DOCS_ALLOWED_ORIGINS | Yes | https://admindoc.stackconsole.io,... | Comma-separated CORS allowed origins | No | Whitelist only official doc domains |
DOCS_FEEDBACK_RATE_LIMIT_PER_MINUTE | No | 30 | Max feedback submissions per client IP per minute | No | 30 |
DOCS_FEEDBACK_BODY_LIMIT_BYTES | No | 16384 | Max JSON request body size (bytes) | No | 16384 (16 KB) |
DOCS_FEEDBACK_ENABLED | No | true | Master operational feature flag | No | true (Set false to disable instantly) |
FEEDBACK_EMAIL_ENABLED | No | false | Enable or disable SMTP notifications | No | false (or true if SMTP configured) |
SMTP_HOST | If email on | smtp.mailgun.org | Outgoing SMTP server hostname | No | Provider host |
SMTP_PORT | No | 587 | SMTP port (typically 587 or 465) | No | 587 |
SMTP_USER | If email on | (none) | SMTP authentication username | YES | Dedicated mail account |
SMTP_PASS | If email on | (none) | SMTP authentication password / token | YES | Strong secret |
SMTP_FROM | No | docs-feedback@stackconsole.io | "From" header address for outgoing emails | No | Verified sending address |
FEEDBACK_NOTIFICATION_EMAIL | If email on | (none) | Internal distribution list recipient | No | e.g. docs-team@stackconsole.io |
Never commit .env or production passwords to Git. The .gitignore file explicitly excludes .env, *.dump, and backups/. In production, supply credentials through secure secrets managers or deployment pipeline environment variables.
6. Database Architecture, Schema & Migrationsโ
PostgreSQL Infrastructureโ
- Container:
stackconsole-feedback-db(postgres:16-alpine) - Data Directory:
/var/lib/postgresql/data/pgdata - Volume Mount:
feedback_pgdata:/var/lib/postgresql/data - Network Isolation: Only exposed to
stackconsole-feedback-network. No public port mapping.
Schema Definition (docs_feedback)โ
The database schema is managed via SQL migrations located in stackconsole-docs-feedback/src/db/migrations/.
-- Initial Schema: 001_initial_schema.sql
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE IF NOT EXISTS docs_feedback (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
feedback_type VARCHAR(32) NOT NULL CHECK (feedback_type IN ('HELPFUL_POSITIVE', 'HELPFUL_NEGATIVE', 'SUGGESTION')),
rating BOOLEAN NULL,
category VARCHAR(64) NULL,
comment TEXT NULL,
email VARCHAR(255) NULL,
page_title VARCHAR(512) NOT NULL,
page_url TEXT NOT NULL,
page_path VARCHAR(512) NOT NULL,
docs_version VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'NEW' CHECK (status IN ('NEW', 'REVIEWING', 'ACCEPTED', 'IMPLEMENTED', 'RESOLVED')),
metadata JSONB NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indexes for high-performance querying and analytics
CREATE INDEX IF NOT EXISTS idx_docs_feedback_created_at ON docs_feedback (created_at DESC);
CREATE INDEX IF NOT EXISTS idx_docs_feedback_page_path ON docs_feedback (page_path);
CREATE INDEX IF NOT EXISTS idx_docs_feedback_type ON docs_feedback (feedback_type);
CREATE INDEX IF NOT EXISTS idx_docs_feedback_status ON docs_feedback (status);
CREATE INDEX IF NOT EXISTS idx_docs_feedback_version ON docs_feedback (docs_version);
Field Definitionsโ
| Column | Type | Constraints | Purpose |
|---|---|---|---|
id | UUID | Primary Key, gen_random_uuid() | Unique immutable record identifier |
feedback_type | VARCHAR(32) | NOT NULL, Check Constraint | HELPFUL_POSITIVE, HELPFUL_NEGATIVE, or SUGGESTION |
rating | BOOLEAN | NULL allowed | true for helpful, false for unhelpful, null for suggestion |
category | VARCHAR(64) | NULL allowed | Defect category or improvement topic |
comment | TEXT | NULL allowed | User comment or detailed suggestion |
email | VARCHAR(255) | NULL allowed | Optional contact email provided by submitter |
page_title | VARCHAR(512) | NOT NULL | Title of the documentation article |
page_url | TEXT | NOT NULL | Full canonical URL where feedback was submitted |
page_path | VARCHAR(512) | NOT NULL | Relative path (e.g. /administration/security/auth-keycloak/) |
docs_version | VARCHAR(32) | NOT NULL | Active documentation version (e.g. 2.0, 3.0) |
status | VARCHAR(32) | NOT NULL, Check Constraint | Lifecycle status: NEW, REVIEWING, ACCEPTED, IMPLEMENTED, RESOLVED |
metadata | JSONB | DEFAULT '{}' | Additional context (e.g. truncated User-Agent, Cloudflare Country) |
created_at | TIMESTAMPTZ | NOT NULL, NOW() | Submission timestamp |
updated_at | TIMESTAMPTZ | NOT NULL, NOW() | Record last modification timestamp |
Status Lifecycleโ
Feedback records transition through an operational lifecycle:
[ NEW ]
โ
โผ
[ REVIEWING ] (Documentation maintainer triaging feedback)
โ
โผ
[ ACCEPTED ] (Proposal validated; documentation update planned)
โ
โผ
[ IMPLEMENTED ] (Documentation changes committed in Merge Request)
โ
โผ
[ RESOLVED ] (Published to production documentation)
NEW: Operational today. Every client submission enters this state.REVIEWING/ACCEPTED/IMPLEMENTED/RESOLVED: Operational today for internal database triaging; will be automated in a future release with GitLab issue tracking.
Idempotent Database Migrationsโ
The migration engine (src/db/migrate.ts) executes automatically upon container startup:
- Verifies or creates the
schema_migrationstracking table:CREATE TABLE IF NOT EXISTS schema_migrations (version VARCHAR(255) PRIMARY KEY,applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); - Reads all
.sqlfiles insrc/db/migrations/in ascending alphabetical order. - Compares files against
schema_migrations. - Executes pending migrations inside individual PostgreSQL transactions (
BEGIN...COMMIT). If an error occurs, the transaction rolls back cleanly (ROLLBACK).
To run migrations manually via CLI:
# From repository root:
npm run feedback:migrate
# Or from stackconsole-docs-feedback directory:
npm run migrate
Migrations are strictly idempotent. Re-running npm run migrate when all migrations have been applied results in zero modifications and exits cleanly. Never manually delete rows from schema_migrations in production.
7. API Referenceโ
The feedback backend exposes REST endpoints under /api/v1/feedback and a backward-compatible alias /api/docs-feedback.
1. Submit Feedback (POST /api/v1/feedback)โ
- Route:
POST /api/v1/feedback(Alias:POST /api/docs-feedback) - Content-Type:
application/json - Rate Limit: 30 requests / minute per client IP
Type A: Positive Rating (HELPFUL_POSITIVE)โ
curl -X POST http://localhost:8001/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "HELPFUL_POSITIVE",
"page_title": "Keycloak Authentication",
"page_url": "https://admindoc.stackconsole.io/administration/security/auth-keycloak/",
"page_path": "/administration/security/auth-keycloak/",
"docs_version": "2.0"
}'
Type B: Negative Rating with Category (HELPFUL_NEGATIVE)โ
Supported categories for negative feedback:
Information is incorrectInformation is missingInstructions are unclearInformation is outdatedBroken linkOther
curl -X POST http://localhost:8001/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "HELPFUL_NEGATIVE",
"category": "Instructions are unclear",
"comment": "Step 4 in realm configuration is missing the redirect URI parameter.",
"page_title": "Keycloak Authentication",
"page_url": "https://admindoc.stackconsole.io/administration/security/auth-keycloak/",
"page_path": "/administration/security/auth-keycloak/",
"docs_version": "2.0"
}'
Type C: Suggestion Modal Submission (SUGGESTION)โ
curl -X POST http://localhost:8001/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "SUGGESTION",
"category": "Documentation Gap / Missing Topic",
"comment": "Please add an end-to-end guide on configuring BGP routing with Calico.",
"email": "cloudadmin@example.com",
"page_title": "Networking Overview",
"page_url": "https://admindoc.stackconsole.io/packages/vpc/",
"page_path": "/packages/vpc/",
"docs_version": "2.0"
}'
Response Formatsโ
Success (201 Created)โ
{
"success": true,
"id": "e241904e-ae3d-4adc-a4cd-11885fb19e34",
"message": "Feedback recorded successfully. Thank you for helping improve StackConsole documentation."
}
Validation Error (400 Bad Request)โ
{
"success": false,
"error": "Invalid feedback submission payload",
"details": [
{
"field": "category",
"message": "Category must be one of: Information is incorrect, Information is missing, Instructions are unclear, Information is outdated, Broken link, Other"
}
]
}
Rate Limit Exceeded (429 Too Many Requests)โ
{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Too many feedback submissions from this IP. Please try again in 58 seconds."
}
Response Header includes: Retry-After: 58.
Service Disabled (503 Service Unavailable)โ
{
"success": false,
"error": "Documentation feedback system is currently disabled by configuration."
}
Database Error (500 Internal Server Error)โ
{
"success": false,
"error": "An internal error occurred while saving your feedback. Please try again later."
}
2. Operational & Health Probesโ
Liveness Probe (GET /health)โ
Verifies that the Fastify process is running and responsive.
curl -s http://localhost:8001/health
{
"status": "ok",
"service": "stackconsole-docs-feedback",
"uptime": 3600.42,
"timestamp": "2026-10-03T10:00:00.000Z"
}
Readiness Probe (GET /ready)โ
Performs a live connectivity test (SELECT 1) against the PostgreSQL connection pool. Returns 200 OK when healthy, or 503 Service Unavailable if database connectivity is lost.
curl -s http://localhost:8001/ready
{
"status": "ready",
"service": "stackconsole-docs-feedback",
"database": "connected"
}
Prometheus Telemetry (GET /metrics)โ
Exposes live operational counters and latency metrics in Prometheus text format:
curl -s http://localhost:8001/metrics
# HELP docs_feedback_submissions_total Total documentation feedback submissions
# TYPE docs_feedback_submissions_total counter
docs_feedback_submissions_total{type="HELPFUL_POSITIVE"} 42
docs_feedback_submissions_total{type="HELPFUL_NEGATIVE"} 12
docs_feedback_submissions_total{type="SUGGESTION"} 8
# HELP docs_feedback_submission_errors_total Total documentation feedback submission errors
# TYPE docs_feedback_submission_errors_total counter
docs_feedback_submission_errors_total{reason="none"} 0
# HELP docs_feedback_rate_limited_total Total requests rejected due to rate limiting
# TYPE docs_feedback_rate_limited_total counter
docs_feedback_rate_limited_total 3
# HELP docs_feedback_database_errors_total Total database operation errors
# TYPE docs_feedback_database_errors_total counter
docs_feedback_database_errors_total 0
# HELP docs_feedback_submission_latency Average latency of feedback submissions in milliseconds
# TYPE docs_feedback_submission_latency gauge
docs_feedback_submission_latency 8.45
8. Frontend Integration & Dynamic Versioningโ
The feedback interface is embedded into Docusaurus through layout component swizzling rather than modifying individual documentation files.
Layout Injection Mechanismโ
- File:
src/theme/DocItem/Layout/index.tsx - Location: Mounted directly above
<DocItemPaginator />on every documentation page:<article>{/* Page content */}</article><DocFeedbacktitle={metadata.title}permalink={metadata.permalink}version={metadata.version}/><DocItemPaginator /> - Zero Markdown Touch Rule: Not a single markdown (
.md/.mdx) file is edited or burdened with feedback boilerplate. Adding or updating articles requires zero feedback maintenance.
Dynamic Documentation Versioningโ
The frontend dynamically captures the version of the documentation being viewed:
DocFeedbackreceivesversionfrom DocusaurususeDoc().metadata.version.- If
metadata.versionis unversioned (e.g. during local editing), it falls back tositeConfig.customFields.currentVersion(2.0). - When new versions (e.g.,
3.0) are branched or released in Docusaurus, the component automatically sends"docs_version": "3.0"with zero code modifications.
Frontend Configuration (docusaurus.config.ts)โ
customFields: {
currentVersion: '2.0',
upcomingVersion: '3.0',
docsFeedbackEnabled: process.env.DOCS_FEEDBACK_ENABLED !== 'false',
docsFeedbackApiUrl:
process.env.DOCS_FEEDBACK_API_URL ||
(process.env.NODE_ENV === 'production' ? '' : 'http://localhost:8001'),
},
Environment Configuration (.env) & Production Buildโ
The Docusaurus build reads DOCS_FEEDBACK_API_URL during compilation to configure the feedback API gateway.
1. Configure Root .envโ
In the root documentation directory (/home/stackconsole/admin-docs):
cat <<EOF > .env
# Feedback API Gateway URL (Production)
DOCS_FEEDBACK_API_URL=https://admindoc.stackconsole.io
# Feedback feature flag (true | false)
DOCS_FEEDBACK_ENABLED=true
EOF
A template is tracked in the repository at .env.example:
cp .env.example .env
2. Execute Production Buildโ
You can build using the .env file directly:
npm run build
Or explicitly provide the variable in the build command:
DOCS_FEEDBACK_API_URL=https://admindoc.stackconsole.io npm run build
Smart Runtime URL Resolutionโ
- In production (
https://admindoc.stackconsole.io), the component automatically routes requests to the same-origin relative path (/api/v1/feedback) or configured domain, safely proxying via Nginx without mixed-content issues or contacting the visitor's local machine. - In local development (
localhost/127.0.0.1), the component automatically falls back tohttp://localhost:8001. - Any trailing slashes (e.g.
https://admindoc.stackconsole.io/) are automatically stripped at runtime to prevent malformed double-slash URLs (...io//api/v1/feedback).
9. Reverse Proxy & Nginx Configurationโ
In production, client browsers communicate with the feedback API via HTTPS on admindoc.stackconsole.io. Nginx terminates TLS, serves the compiled Docusaurus static assets, and reverse-proxies API requests to the Fastify microservice listening on loopback port 8001.
Complete Production Nginx Server Blockโ
Place this configuration inside /etc/nginx/sites-available/admindoc.stackconsole.io.conf (or symlinked to /etc/nginx/sites-enabled/):
# ==============================================================================
# StackConsole Administrator Documentation (Docusaurus + Feedback Microservice)
# ==============================================================================
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name admindoc.stackconsole.io;
# ACME challenge for Let's Encrypt / Certbot
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Primary Virtual Host
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name admindoc.stackconsole.io;
# TLS / SSL Certificates
ssl_certificate /etc/letsencrypt/live/admindoc.stackconsole.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admindoc.stackconsole.io/privkey.pem;
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 off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Root Directory for Docusaurus Compiled Build Output
root /home/stackconsole/admin-docs/build;
index index.html;
# --------------------------------------------------------------------------
# 1. Docusaurus SPA Static Asset Routing
# --------------------------------------------------------------------------
location / {
try_files $uri $uri/ $uri.html /index.html;
}
# Static asset caching (JS, CSS, SVGs, images)
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|otf|ttf|svg)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
access_log off;
}
# --------------------------------------------------------------------------
# 2. StackConsole Documentation Feedback Reverse Proxy
# --------------------------------------------------------------------------
# Canonical API route (used by in-portal thumbs & suggestions)
location /api/v1/feedback {
proxy_pass http://127.0.0.1:8001/api/v1/feedback;
proxy_http_version 1.1;
# Client identification headers for rate-limiting & logging
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;
# Timeouts
proxy_connect_timeout 5s;
proxy_read_timeout 15s;
proxy_send_timeout 15s;
# Hardened request payload limits (Feedback body limit: 16 KB)
client_max_body_size 64k;
}
# Backward-compatible alias route
location /api/docs-feedback {
proxy_pass http://127.0.0.1:8001/api/docs-feedback;
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;
proxy_connect_timeout 5s;
proxy_read_timeout 15s;
proxy_send_timeout 15s;
client_max_body_size 64k;
}
# Internal monitoring endpoints (Restricted to internal monitoring subnet)
location /api/feedback/metrics {
# Allow Prometheus scrapers only
allow 10.0.0.0/8;
allow 127.0.0.1;
deny all;
proxy_pass http://127.0.0.1:8001/metrics;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
Testing and Reloading Nginxโ
- Test configuration syntax:
sudo nginx -t
- Reload Nginx without interrupting active connections:
sudo systemctl reload nginx
- Test reverse proxy connectivity:
(Expected response:# Test feedback API endpoint directly via Nginxcurl -I https://admindoc.stackconsole.io/api/v1/feedback
HTTP/2 400orHTTP/2 404/405from Fastify, NOT Nginx 502 Bad Gateway or 404 Not Found).
10. Production Deployment Runbookโ
Follow this phase-by-phase runbook to deploy the feedback subsystem to a production environment.
Phase 1: Host Preparationโ
- Ensure Docker and Docker Compose v2 are installed on the production host.
- Confirm port
8001is not bound by another service:ss -tuln | grep 8001 || echo "Port 8001 is free"
Phase 2: Configure Production Secretsโ
- Navigate to the feedback directory on the deployment host:
cd /opt/stackconsole/admin-docs/stackconsole-docs-feedback
- Generate a cryptographically strong password for PostgreSQL:
PG_PASS=$(openssl rand -hex 24)
- Populate
.envwith production values:cat <<EOF > .envNODE_ENV=productionAPP_PORT=8001APP_HOST=0.0.0.0POSTGRES_HOST=feedback-dbPOSTGRES_PORT=5432POSTGRES_DB=stackconsole_feedbackPOSTGRES_USER=feedback_appPOSTGRES_PASSWORD=${PG_PASS}DOCS_ALLOWED_ORIGINS=https://admindoc.stackconsole.ioDOCS_FEEDBACK_RATE_LIMIT_PER_MINUTE=30DOCS_FEEDBACK_ENABLED=trueFEEDBACK_EMAIL_ENABLED=falseEOFchmod 600 .env
Phase 3: Launch Services via Docker Composeโ
Build and start the containers in detached mode:
docker compose up -d --build
Phase 4: Verify Container & Database Healthโ
docker compose ps
Ensure both stackconsole-docs-feedback and stackconsole-feedback-db report status running (healthy).
Check startup migration logs:
docker compose logs docs-feedback | grep -i "migration"
Expected Output: Database schema ready. Applied 1 pending migrations.
Phase 5: Configure Reverse Proxyโ
Add the Nginx configuration snippet documented in Section 9 and reload Nginx:
sudo nginx -t && sudo systemctl reload nginx
Phase 6: Configure Environment & Build Frontend Bundleโ
In the repository root directory (/home/stackconsole/admin-docs):
# 1. Populate root .env configuration
cat <<EOF > .env
DOCS_FEEDBACK_API_URL=https://admindoc.stackconsole.io
DOCS_FEEDBACK_ENABLED=true
EOF
# 2. Build production static bundle (reads .env automatically)
npm run build
# Or explicitly supply variable during build:
# DOCS_FEEDBACK_API_URL=https://admindoc.stackconsole.io npm run build
Phase 7: Execute Post-Deployment Smoke Testโ
Run the automated verification steps detailed in Section 12.
11. Production Pre-Flight Checklistโ
Before declaring the deployment operational, verify every item on this checklist:
- DNS & Routing:
admindoc.stackconsole.ioresolves to production reverse proxy. - TLS Certificate: Valid SSL/TLS certificate active on
admindoc.stackconsole.io. - Docker Service: Docker daemon active and configured with
restart: unless-stopped. - Network Isolation: Verified
docker port stackconsole-feedback-dbreturns empty (port 5432 is not exposed to public host interfaces). - Database Password: High-entropy secret generated and stored securely in
.env. - Persistent Volume: Named volume
feedback_pgdataverified withdocker volume inspect feedback_pgdata. - Database Migrations: Verified table
docs_feedbackand indexes exist in PostgreSQL. - Reverse Proxy: Nginx routes
/api/v1/feedbacktohttp://127.0.0.1:8001/api/v1/feedback. - CORS Configuration:
DOCS_ALLOWED_ORIGINSmatcheshttps://admindoc.stackconsole.io. - Rate Limiting: Verified sliding window limits clients to 30 requests/minute.
- Health Endpoint:
curl -f http://127.0.0.1:8001/healthreturns200 OK. - Readiness Endpoint:
curl -f http://127.0.0.1:8001/readyreturns200 OKwith database connected. - Smoke Test Submission: Submitted test thumbs-up and improvement suggestion, verified row in PostgreSQL.
- Backup Verification: Executed
./scripts/backup-feedback-db.sh, verified non-empty.dumpfile created. - Zero PII in Logs: Verified application logs do not print emails or comment bodies in plain text.
- Frontend Build: Docusaurus compiles cleanly with zero SSR errors and zero broken links.
12. Post-Deployment Smoke Test Runbookโ
Perform this sequential smoke test immediately following any production deployment or upgrade:
Step 1: Probe Livenessโ
curl -i https://admindoc.stackconsole.io/health
Expected: HTTP 200 OK, JSON status: "ok".
Step 2: Probe Readinessโ
curl -i https://admindoc.stackconsole.io/ready
Expected: HTTP 200 OK, JSON database: "connected".
Step 3: Positive Feedback Submission Testโ
curl -i -X POST https://admindoc.stackconsole.io/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "HELPFUL_POSITIVE",
"page_title": "Smoke Test Page",
"page_url": "https://admindoc.stackconsole.io/smoke-test",
"page_path": "/smoke-test",
"docs_version": "2.0"
}'
Expected: HTTP 201 Created, JSON success: true, UUID returned.
Step 4: Negative Feedback Submission Testโ
curl -i -X POST https://admindoc.stackconsole.io/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "HELPFUL_NEGATIVE",
"category": "Instructions are unclear",
"comment": "Smoke test automated negative feedback.",
"page_title": "Smoke Test Page",
"page_url": "https://admindoc.stackconsole.io/smoke-test",
"page_path": "/smoke-test",
"docs_version": "2.0"
}'
Expected: HTTP 201 Created, JSON success: true.
Step 5: Suggestion Submission Testโ
curl -i -X POST https://admindoc.stackconsole.io/api/v1/feedback \
-H "Content-Type: application/json" \
-d '{
"feedback_type": "SUGGESTION",
"category": "Smoke Test Proposal",
"comment": "Verifying end-to-end suggestion modal flow.",
"email": "test-admin@stackconsole.io",
"page_title": "Smoke Test Page",
"page_url": "https://admindoc.stackconsole.io/smoke-test",
"page_path": "/smoke-test",
"docs_version": "2.0"
}'
Expected: HTTP 201 Created, JSON success: true.
Step 6: Verify Persistence in PostgreSQLโ
docker exec -t stackconsole-feedback-db psql -U feedback_app -d stackconsole_feedback -c \
"SELECT id, feedback_type, category, docs_version, created_at FROM docs_feedback WHERE page_path = '/smoke-test';"
Expected: 3 rows returned corresponding to steps 3, 4, and 5.
Step 7: Clean Up Smoke Test Dataโ
docker exec -t stackconsole-feedback-db psql -U feedback_app -d stackconsole_feedback -c \
"DELETE FROM docs_feedback WHERE page_path = '/smoke-test';"
13. Backup & Restore Proceduresโ
PostgreSQL automated backups are executed using custom-format compressed binary dumps (pg_dump -F c).
Backup Script (scripts/backup-feedback-db.sh)โ
The backup script connects to the running PostgreSQL container via standard input streams (docker exec -i) to prevent binary stream corruption, writes to backups/feedback/, and automatically purges backups older than 14 days.
# Execute backup from repository root:
./scripts/backup-feedback-db.sh
Environment variables supported by the script:
CONTAINER_NAME: Target container (Default:stackconsole-feedback-db)DB_NAME: Database name (Default:stackconsole_feedback)DB_USER: Database user (Default:feedback_app)BACKUP_DIR: Directory to write backups (Default:$(pwd)/backups/feedback)RETENTION_DAYS: Retention window (Default:14)
Sample output:
============================================================
StackConsole Feedback Database Backup
Target Container: stackconsole-feedback-db
Database: stackconsole_feedback
User: feedback_app
Destination: .../backups/feedback/feedback_backup_20261003_140000.dump
============================================================
Running pg_dump inside stackconsole-feedback-db...
Backup successfully created: .../backups/feedback/feedback_backup_20261003_140000.dump (48K)
Rotating backups older than 14 days...
Backup procedure completed successfully.
Automated Nightly Cron Jobโ
To schedule automatic nightly backups at 02:00 UTC:
0 2 * * * cd /opt/stackconsole/admin-docs && ./scripts/backup-feedback-db.sh >> /var/log/feedback-backup.log 2>&1
Restore Script (scripts/restore-feedback-db.sh)โ
The restore script executes pg_restore with --clean, --if-exists, --no-owner, and --no-privileges.
Restoring a backup overwrites existing data in the target database. Always generate a pre-restore safety dump before performing a restore.
Interactive Restoreโ
./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_20261003_140000.dump
The script prompts for explicit confirmation (yes) before executing.
Non-Interactive Restore (Automated Disaster Recovery)โ
Use the --force flag in automated recovery scripts:
./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_20261003_140000.dump --force
Verification Post-Restoreโ
The script automatically queries and outputs the row count of docs_feedback upon completion:
Verifying row counts in docs_feedback...
Restore finished successfully. Current docs_feedback rows: 33
14. Disaster Recovery Scenariosโ
| Scenario | Symptoms | Impact | Detection | Step-by-Step Recovery Procedure | Verification |
|---|---|---|---|---|---|
| A. API Container Failure | 502 Bad Gateway on /api/v1/feedback. Portal remains readable. | Feedback cannot be submitted. Docs browsing unaffected. | docker compose ps shows docs-feedback exited. Alert on /health. | 1. Inspect logs: docker compose logs docs-feedback2. Restart container: docker compose restart docs-feedback3. If image corrupted, rebuild: docker compose build docs-feedback && docker compose up -d docs-feedback | curl -f http://127.0.0.1:8001/health returns 200 OK. |
| B. PostgreSQL Container Failure | /ready returns 503 Service Unavailable. API logs connection errors. | Feedback submissions return HTTP 500. Docs browsing unaffected. | Monitoring probe on /ready fails. docker compose ps shows feedback-db exited. | 1. Check database logs: docker compose logs feedback-db2. Verify disk space: df -h3. Restart container: docker compose restart feedback-db4. Fastify automatically recovers once database is healthy. | curl -f http://127.0.0.1:8001/ready returns 200 OK. |
| C. Unplanned Host Reboot | All services offline immediately after reboot. | Portal and feedback services unavailable until Docker starts. | System uptime reset; ping alerts. | 1. Ensure Docker daemon starts on boot: sudo systemctl enable docker2. Docker Compose containers are configured with restart: unless-stopped and will automatically resume with volume intact. | docker compose ps confirms both containers running (healthy). |
| D. Docker Daemon Crash | docker CLI hangs or returns connection refused to daemon socket. | All containerized services halted. | Systemd journal: journalctl -u docker -e. | 1. Restart daemon: sudo systemctl restart docker2. Start compose stack if not auto-started: docker compose up -d | Check container health with docker compose ps. |
| E. PostgreSQL Data Corruption | PostgreSQL engine enters crash loop; logs show corrupted block or WAL error. | Database cannot start or read tables. Submissions fail. | docker compose logs feedback-db shows PANIC or database corruption. | 1. Stop containers: docker compose down2. Remove corrupted volume: docker volume rm stackconsole-docs-feedback_feedback_pgdata3. Recreate fresh volume & start DB: docker compose up -d feedback-db4. Restore latest valid backup: ./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_LATEST.dump --force5. Start API: docker compose up -d docs-feedback | Run post-deployment smoke test. Verify row count matches backup. |
| F. Accidental Database Deletion | Table docs_feedback dropped or corrupted by errant command. | Submissions return HTTP 500; queries fail. | API logs relation "docs_feedback" does not exist. | 1. Restore from latest backup dump: ./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_LATEST.dump --force2. Verify table exists: docker exec -t stackconsole-feedback-db psql -U feedback_app -d stackconsole_feedback -c "\dt" | Submit test payload and verify persistence. |
| G. Bad Application Deployment | API crashes on start or fails validation checks after upgrade. | New feedback submissions fail. | Container restart loop; /health fails. | 1. Roll back to previous Git commit or image tag. 2. Rebuild: docker compose up -d --build docs-feedback3. If frontend issue, toggle killswitch: DOCS_FEEDBACK_ENABLED=false | Verify /health and /ready probes. |
| H. SMTP Outage | External mail provider (e.g. Mailgun, SendGrid) unreachable or rejected credentials. | Zero impact on user submissions. Submissions are persisted in PostgreSQL. Warning in logs. | Application log: Failed to deliver feedback email notification. Submission preserved in database. | 1. Verify SMTP credentials in .env2. Test SMTP reachability from host: nc -zv smtp.mailgun.org 5873. Submissions remain safely stored in docs_feedback and can be audited via SQL. | Check application logs after fixing SMTP credentials. |
| I. Reverse Proxy Outage | Nginx fails to start or returns 502 Bad Gateway. | Portal and feedback API unreachable from WAN. | Nginx service alerts: systemctl status nginx. | 1. Test Nginx configuration: sudo nginx -t2. Check upstream port 8001: curl -I http://127.0.0.1:8001/health3. Restart Nginx: sudo systemctl restart nginx | Verify portal and API reachability via HTTPS. |
15. Safe Upgrades & Rollbacksโ
Safe Upgrade Procedureโ
Follow these steps when deploying a new version of the feedback subsystem:
- Step 1: Execute Pre-Upgrade Backup:
./scripts/backup-feedback-db.sh
- Step 2: Pull Latest Version:
git pull origin main
- Step 3: Run Test Suite Locally:
npm test --prefix stackconsole-docs-feedback
- Step 4: Build Container Image & Apply Migrations:
Migrations execute automatically during startup.cd stackconsole-docs-feedbackdocker compose up -d --build
- Step 5: Verify Health & Smoke Test:
curl -s http://localhost:8001/ready
Multi-Level Rollback Strategyโ
If an issue is detected during or after deployment, choose the appropriate rollback tier:
Tier 1: Frontend Killswitch (Zero Downtime)โ
If the backend is degraded or under maintenance, disable feedback rendering immediately on the frontend without touching containers or databases:
- Set
docsFeedbackEnabled: falseindocusaurus.config.tsor deploy frontend withDOCS_FEEDBACK_ENABLED=false. - The
DocFeedbackwidget disappears instantly across all documentation pages.
Tier 2: Backend Container Rollbackโ
If the newly deployed container version contains an application bug:
- Check out the previous stable release tag:
git checkout v1.0.0 # (or previous Git commit SHA)
- Rebuild and restart the container:
Note: Persistent database volumecd stackconsole-docs-feedbackdocker compose up -d --build docs-feedback
feedback_pgdataremains intact.
Tier 3: Database Point-in-Time Restoreโ
If an errant schema change or data corruption occurred:
- Revert to the pre-upgrade backup created in Step 1:
./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_PRE_UPGRADE.dump --force
Database schema migrations are designed to be strictly backward-compatible and additive. Downward migrations (DROP COLUMN, DROP TABLE) are never performed automatically in production.
16. Security & Privacy Controlsโ
The feedback system is designed with defense-in-depth principles:
Anonymous Endpoint Protectionโ
Because the feedback endpoint is publicly accessible without user authentication, it enforces strict perimeter controls:
- Rate Limiting: Custom sliding-window limiter (
MemoryRateLimiter) caps requests at 30 requests per minute per IP address. - Request Size Ceiling: Enforced
16 KBpayload cap (DOCS_FEEDBACK_BODY_LIMIT_BYTES). Requests larger than 16 KB are rejected with HTTP 413. - Strict Input Validation: All strings are sanitized to strip unprintable control characters and null bytes. Maximum lengths are enforced:
- Page title / path: 512 characters
- Page URL: 2,048 characters
- Suggestion text: 5,000 characters
- Negative comment: 2,000 characters
- Email: 255 characters (validated against RFC 5322 email regex)
- Parameterized SQL: Database queries use node-postgres parameterized bindings (
$1, $2, ...). Dynamic SQL string concatenation is strictly prohibited, eliminating SQL injection vectors. - Hardened HTTP Headers:
@fastify/helmetapplies security headers (X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Strict-Transport-Security).
Zero-PII Logging Policyโ
The structured logger (src/observability/logger.ts) enforces customer privacy:
- Submitter emails and feedback comment bodies are never logged in cleartext.
- Email domains and a one-way SHA-256 hash prefix (e.g.
sha256:abcd1234...) are used exclusively for fraud/abuse correlation without recording identifiable personal data. - Application logs contain only metadata:
feedbackId,feedbackType,pagePath,docsVersion,latencyMs.
Network & Infrastructure Securityโ
- Container Isolation: PostgreSQL container runs on an internal Docker bridge with zero port bindings to
0.0.0.0. It cannot be probed or connected to from external networks. - Least-Privilege Execution: The Node.js application container runs as an unprivileged system user (
USER nodeinDockerfile). Root execution inside the container is blocked. - Client Bundle Cleanliness: Zero database credentials, SMTP passwords, or internal tokens exist in frontend Docusaurus bundles. All secrets are confined to the backend
.env.
17. Scalability Boundaries & Future Architectureโ
Current Supported Deployment Modelโ
The current implementation is designed and validated for:
- Topology: Single-instance
docs-feedbackFastify container + Single-instancefeedback-dbPostgreSQL container. - Capacity: Easily handles hundreds of concurrent documentation readers and up to 30 submissions/minute per IP. Typical documentation feedback submission frequency is on the order of tens to hundreds of submissions per day, well within single-instance capacity.
- Storage: PostgreSQL database disk usage is lightweight (under 50 MB for tens of thousands of submissions).
Scalability Boundaries & Requirements for Multi-Replica Deploymentsโ
When scaling out horizontally across multiple API replicas behind a load balancer, operators must take note of the following architectural boundary:
- Distributed Rate Limiting (PLANNED / FUTURE):
- Current Implementation: The sliding-window rate limiter is held in-memory per Fastify process (
MemoryRateLimiter). - Scaling Requirement: In a multi-replica deployment, an IP could submit 30 requests to Replica A and 30 requests to Replica B.
- Production Solution: Multi-replica deployments must delegate rate limiting to the ingress reverse proxy (e.g. Nginx
limit_req_zone $binary_remote_addr zone=feedback_limit:10m rate=30r/m;) or introduce a shared Redis key-value store. Redis is not currently required or deployed.
- Current Implementation: The sliding-window rate limiter is held in-memory per Fastify process (
- Centralized Logging & Telemetry:
- Prometheus scrapers should aggregate
/metricsacross all running instances or scrape an ingress metric aggregator.
- Prometheus scrapers should aggregate
- Database Read Replicas (PLANNED / FUTURE):
- If analytics or triage query loads increase significantly, a PostgreSQL read replica can be provisioned using standard streaming replication. The write path remains focused on the primary node.
18. Operations Runbookโ
Daily Operational Tasksโ
- Health Verification:
curl -s https://admindoc.stackconsole.io/healthcurl -s https://admindoc.stackconsole.io/ready
- Inspect Error Logs:
docker compose logs --tail=100 docs-feedback | grep -i "error" || echo "Zero errors reported"
- Check Prometheus Metrics:
curl -s http://127.0.0.1:8001/metrics | grep "docs_feedback_"
Weekly Maintenance Tasksโ
- Verify Backup Generation:
Confirm that nightly backup files are being generated and rotated in
backups/feedback/:ls -lh backups/feedback/ - Check Container Disk Usage:
docker system df
- Review Triage Volume:
Query count of unresolved feedback submissions:
docker exec -t stackconsole-feedback-db psql -U feedback_app -d stackconsole_feedback -c \"SELECT status, feedback_type, COUNT(*) FROM docs_feedback GROUP BY status, feedback_type;"
Secret Rotation Procedure (PostgreSQL Password)โ
To rotate the PostgreSQL password without data loss:
- Connect to PostgreSQL and update the user password:
NEW_PASS=$(openssl rand -hex 24)docker exec -t stackconsole-feedback-db psql -U postgres -d stackconsole_feedback -c \"ALTER USER feedback_app WITH PASSWORD '${NEW_PASS}';"
- Update
.envinstackconsole-docs-feedback/:sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=${NEW_PASS}/" .env - Restart the Fastify API container:
docker compose up -d docs-feedback
- Verify readiness:
curl -s http://localhost:8001/ready
19. Troubleshooting Matrixโ
| Symptom | Probable Cause | Diagnostic Command | Resolution Procedure |
|---|---|---|---|
GET /ready returns 503 | PostgreSQL container is starting or connection pool timeout | docker compose logs feedback-db | Check if PostgreSQL container is running. Verify POSTGRES_HOST, POSTGRES_USER, and POSTGRES_PASSWORD match in .env. |
| API Container Won't Start | Port 8001 conflict or schema validation error in .env | docker compose logs docs-feedback | Check port binding with ss -tuln | grep 8001. Fix malformed environment variables in .env. |
| PostgreSQL Won't Start | File permissions issue on volume or disk space exhausted | docker compose logs feedback-db and df -h | Ensure host has free disk space. Check volume permissions. |
| Database Connection Refused | PostgreSQL not yet accepting connections or wrong host | docker exec -t stackconsole-docs-feedback nc -zv feedback-db 5432 | Verify depends_on healthcheck condition in docker-compose.yml. |
| 429 Too Many Requests | Submitter exceeded 30 requests/minute | Inspect /metrics counter docs_feedback_rate_limited_total | Expected behavior under spam prevention. Client must wait for Retry-After seconds. |
| CORS Policy Error in Browser | Browser origin does not match DOCS_ALLOWED_ORIGINS | Browser DevTools Console | Update DOCS_ALLOWED_ORIGINS in .env to include the client domain, then restart docs-feedback. |
| Frontend Returns "Could not record feedback" | Reverse proxy misconfiguration or feedback API offline | curl -i http://127.0.0.1:8001/health | Check Nginx reverse proxy logs (/var/log/nginx/error.log). Ensure location /api/v1/feedback is forwarded. |
| Feedback Submits Successfully But No Emails Arrive | FEEDBACK_EMAIL_ENABLED=false or SMTP credentials invalid | docker compose logs docs-feedback | grep -i "mailer" | By design, database persistence succeeds even if SMTP fails. Check FEEDBACK_EMAIL_ENABLED=true and verify credentials. |
| Backup Script Fails | Container name mismatch or Docker daemon inaccessible | ./scripts/backup-feedback-db.sh output | Ensure container stackconsole-feedback-db is running. Check Docker permissions. |
| Restore Script Fails | Backup file missing, corrupted, or incompatible format | file <backup-dump-file> | Ensure dump was generated with pg_dump -F c. Do not use SQL plaintext dumps with pg_restore. |
| Docker Volume Missing After Restart | Ran docker compose down -v accidentally | docker volume ls | grep feedback | Restore database immediately from the latest .dump file using ./scripts/restore-feedback-db.sh. |
| Reverse Proxy Returns 502 | stackconsole-docs-feedback container is down | docker compose ps docs-feedback | Restart container: docker compose up -d docs-feedback. |
| Feedback Widget Not Visible on Docs Page | Feature flag disabled in Docusaurus | Check siteConfig.customFields.docsFeedbackEnabled | Ensure DOCS_FEEDBACK_ENABLED=true during npm run build. |
| Wrong Documentation Version Recorded | Custom field currentVersion misconfigured | Query SELECT DISTINCT docs_version FROM docs_feedback; | Verify currentVersion in docusaurus.config.ts. Version is dynamically extracted per page. |
20. CI/CD & Pipeline Workflowโ
Continuous Integration for the feedback service is defined in stackconsole-docs-feedback/.gitlab-ci.yml.
Pipeline Stages & Jobsโ
- Image:
node:20-alpine(standardized across company pipelines). - Stages:
validate:typecheck: Runsnpm run typecheckto ensure strict TypeScript type safety.dependency-audit: Runsnpm audit --audit-level=highto detect vulnerable third-party dependencies.
test:unit-and-api-tests: Runsnpm testexecuting Vitest/Jest unit and API route test suites.
build:docker-build: Usesdocker:24.0.5-dindto build container images tagged with$CI_COMMIT_SHAandlatestonmainand release tags.
The GitLab CI pipeline validates code and packages Docker container images. It does not autonomously deploy to production environments. Production deployment requires operator execution following the Production Deployment Runbook.
21. Future GitLab Internal Triage Workflowโ
(Status: PLANNED / FUTURE ARCHITECTURE)
In a future iteration, an internal background triage worker will synchronize accepted feedback records directly into private internal GitLab issues for documentation engineering teams.
Customer Submission (admindoc.stackconsole.io)
โ
โผ
Feedback API Gateway (:8001)
โ
โผ
PostgreSQL Database (`docs_feedback`)
โ
โผ
Documentation Team Triage (`status: 'ACCEPTED'`)
โ
โผ
[FUTURE] Automated GitLab Worker (Creates Private GitLab Issue)
โ
โผ
Documentation Update Merge Request
โ
โผ
Resolution (`status: 'RESOLVED'`)
- Customer Anonymity Preserved: Customers will never require a GitLab account or external authentication. The interaction remains 100% within the documentation portal.
- Internal Security: GitLab issue creation will occur strictly server-side via internal access tokens with zero token exposure to client browsers.
22. AI Governance & Change Managementโ
All modifications to the documentation feedback subsystem are governed by the StackConsole Canonical AI Engineering Standards (STD-AI-001 and STD-AI-002).
Mandatory Workflow for Future Changesโ
- Read Core Context: Inspect
.ai/context/project.md,architecture.md,technology.md, andconstraints.md. - Review Architectural Invariants:
- Feedback submission must remain anonymous and friction-free.
- Documentation portal browsing must never fail if backend microservices are offline.
- Zero secrets or customer PII in frontend bundles, logs, or metrics.
- PostgreSQL port
5432must never be exposed to public host interfaces.
- Change Governance & Approval Gates:
- Level 1 (Low Risk): Documentation updates, comment fixes.
- Level 2 (Normal): Bug fixes, UI enhancements, test additions. Requires 1 Peer Reviewer + CI pass.
- Level 3 (High Impact): Database schema alterations, new API endpoints, security changes. Requires Architectural Decision Record (ADR) + 2 Approvers.
- Level 4 (Critical): Auth modifications, PII handling, secret rotation. Requires Security Sign-off.
- Independent Validation: Changes must be validated in live execution environments (Docker, PostgreSQL, Docusaurus build, link validation) and recorded in
.ai/reviews/.