Skip to main content

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):

SubsystemPrimary RoleHost PortDatabase / EnginePublic Exposure
Admin Docs PortalStatic documentation site80 / 443Docusaurus v3 Static HTMLPublic WAN
Docs Feedback ServiceIn-portal rating & proposal persistence8001PostgreSQL 16 AlpineReverse Proxy (/api/v1/feedback)
Docs AI AssistantGrounded RAG query answering8000Qdrant Vector DB & OllamaReverse Proxy (/api/v1/ai/)

2. System Architecture & Component Topologyโ€‹

Component Breakdownโ€‹

  1. Frontend UI Components (DocFeedback & FeedbackModal):

    • Location: src/components/Feedback/DocFeedback.tsx and src/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.0 or upcoming 3.0).
    • Accessibility compliant: supports full keyboard navigation, focus trapping in modal dialogs, and ESC key dismissal.
  2. 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/helmet and strict domain origin whitelisting via @fastify/cors.
    • Structured JSON logging (src/observability/logger.ts) with mandatory zero-PII redaction.
    • Prometheus metrics exposition (GET /metrics).
  3. 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 5432 is unmapped to the host interface. It is accessible exclusively by stackconsole-docs-feedback through the internal bridge.
    • Storage Volume: Persistent named Docker volume feedback_pgdata mounted at /var/lib/postgresql/data.
  4. 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.

3. Prerequisitesโ€‹

Local Development Requirementsโ€‹

  • Operating System: macOS 12+, Ubuntu 20.04+, or Debian 11+
  • Node.js: v20.x or higher (node -v)
  • Package Manager: npm v10.x or higher (npm -v)
  • Docker: Docker Engine 20.10.x or higher (docker -v)
  • Docker Compose: Compose plugin v2.0.x or 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 to 127.0.0.1:8001 or reverse-proxied internally.
    • Port 5432: NEVER exposed publicly.

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 VariableRequiredDefault ValueDescriptionSecret?Production Recommendation
NODE_ENVYesproductionNode runtime environment (production, development, test)Noproduction
APP_PORTNo8001TCP port for the Fastify feedback serviceNo8001
APP_HOSTNo0.0.0.0IP bind address for FastifyNo0.0.0.0
POSTGRES_HOSTYesfeedback-dbDatabase hostname or Docker service nameNofeedback-db (Docker) or private IP
POSTGRES_PORTNo5432PostgreSQL listening portNo5432
POSTGRES_DBYesstackconsole_feedbackPostgreSQL database nameNostackconsole_feedback
POSTGRES_USERYesfeedback_appPostgreSQL application userNoDedicated unprivileged user
POSTGRES_PASSWORDYes(none)Strong password for PostgreSQL userYESStrong random 32+ char secret
POSTGRES_SSLNofalseEnable TLS/SSL for database connectionNofalse in Docker network; true if external
POSTGRES_MAX_CONNECTIONSNo20Max client connections in pg poolNo20 to 50
DATABASE_URLNo(none)Complete connection string (overrides individual POSTGRES_* vars)YESOptional alternative
DOCS_ALLOWED_ORIGINSYeshttps://admindoc.stackconsole.io,...Comma-separated CORS allowed originsNoWhitelist only official doc domains
DOCS_FEEDBACK_RATE_LIMIT_PER_MINUTENo30Max feedback submissions per client IP per minuteNo30
DOCS_FEEDBACK_BODY_LIMIT_BYTESNo16384Max JSON request body size (bytes)No16384 (16 KB)
DOCS_FEEDBACK_ENABLEDNotrueMaster operational feature flagNotrue (Set false to disable instantly)
FEEDBACK_EMAIL_ENABLEDNofalseEnable or disable SMTP notificationsNofalse (or true if SMTP configured)
SMTP_HOSTIf email onsmtp.mailgun.orgOutgoing SMTP server hostnameNoProvider host
SMTP_PORTNo587SMTP port (typically 587 or 465)No587
SMTP_USERIf email on(none)SMTP authentication usernameYESDedicated mail account
SMTP_PASSIf email on(none)SMTP authentication password / tokenYESStrong secret
SMTP_FROMNodocs-feedback@stackconsole.io"From" header address for outgoing emailsNoVerified sending address
FEEDBACK_NOTIFICATION_EMAILIf email on(none)Internal distribution list recipientNoe.g. docs-team@stackconsole.io
Security Notice: Never Commit Secrets

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โ€‹

ColumnTypeConstraintsPurpose
idUUIDPrimary Key, gen_random_uuid()Unique immutable record identifier
feedback_typeVARCHAR(32)NOT NULL, Check ConstraintHELPFUL_POSITIVE, HELPFUL_NEGATIVE, or SUGGESTION
ratingBOOLEANNULL allowedtrue for helpful, false for unhelpful, null for suggestion
categoryVARCHAR(64)NULL allowedDefect category or improvement topic
commentTEXTNULL allowedUser comment or detailed suggestion
emailVARCHAR(255)NULL allowedOptional contact email provided by submitter
page_titleVARCHAR(512)NOT NULLTitle of the documentation article
page_urlTEXTNOT NULLFull canonical URL where feedback was submitted
page_pathVARCHAR(512)NOT NULLRelative path (e.g. /administration/security/auth-keycloak/)
docs_versionVARCHAR(32)NOT NULLActive documentation version (e.g. 2.0, 3.0)
statusVARCHAR(32)NOT NULL, Check ConstraintLifecycle status: NEW, REVIEWING, ACCEPTED, IMPLEMENTED, RESOLVED
metadataJSONBDEFAULT '{}'Additional context (e.g. truncated User-Agent, Cloudflare Country)
created_atTIMESTAMPTZNOT NULL, NOW()Submission timestamp
updated_atTIMESTAMPTZNOT 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:

  1. Verifies or creates the schema_migrations tracking table:
    CREATE TABLE IF NOT EXISTS schema_migrations (
    version VARCHAR(255) PRIMARY KEY,
    applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    );
  2. Reads all .sql files in src/db/migrations/ in ascending alphabetical order.
  3. Compares files against schema_migrations.
  4. 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
Migration Safety

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 incorrect
  • Information is missing
  • Instructions are unclear
  • Information is outdated
  • Broken link
  • Other
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>
    <DocFeedback
    title={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:

  1. DocFeedback receives version from Docusaurus useDoc().metadata.version.
  2. If metadata.version is unversioned (e.g. during local editing), it falls back to siteConfig.customFields.currentVersion (2.0).
  3. 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 to http://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โ€‹

  1. Test configuration syntax:
    sudo nginx -t
  2. Reload Nginx without interrupting active connections:
    sudo systemctl reload nginx
  3. Test reverse proxy connectivity:
    # Test feedback API endpoint directly via Nginx
    curl -I https://admindoc.stackconsole.io/api/v1/feedback
    (Expected response: HTTP/2 400 or HTTP/2 404/405 from 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โ€‹

  1. Ensure Docker and Docker Compose v2 are installed on the production host.
  2. Confirm port 8001 is not bound by another service:
    ss -tuln | grep 8001 || echo "Port 8001 is free"

Phase 2: Configure Production Secretsโ€‹

  1. Navigate to the feedback directory on the deployment host:
    cd /opt/stackconsole/admin-docs/stackconsole-docs-feedback
  2. Generate a cryptographically strong password for PostgreSQL:
    PG_PASS=$(openssl rand -hex 24)
  3. Populate .env with production values:
    cat <<EOF > .env
    NODE_ENV=production
    APP_PORT=8001
    APP_HOST=0.0.0.0
    POSTGRES_HOST=feedback-db
    POSTGRES_PORT=5432
    POSTGRES_DB=stackconsole_feedback
    POSTGRES_USER=feedback_app
    POSTGRES_PASSWORD=${PG_PASS}
    DOCS_ALLOWED_ORIGINS=https://admindoc.stackconsole.io
    DOCS_FEEDBACK_RATE_LIMIT_PER_MINUTE=30
    DOCS_FEEDBACK_ENABLED=true
    FEEDBACK_EMAIL_ENABLED=false
    EOF
    chmod 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.io resolves 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-db returns 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_pgdata verified with docker volume inspect feedback_pgdata.
  • Database Migrations: Verified table docs_feedback and indexes exist in PostgreSQL.
  • Reverse Proxy: Nginx routes /api/v1/feedback to http://127.0.0.1:8001/api/v1/feedback.
  • CORS Configuration: DOCS_ALLOWED_ORIGINS matches https://admindoc.stackconsole.io.
  • Rate Limiting: Verified sliding window limits clients to 30 requests/minute.
  • Health Endpoint: curl -f http://127.0.0.1:8001/health returns 200 OK.
  • Readiness Endpoint: curl -f http://127.0.0.1:8001/ready returns 200 OK with 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 .dump file 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.

Destructive Operation Warning

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โ€‹

ScenarioSymptomsImpactDetectionStep-by-Step Recovery ProcedureVerification
A. API Container Failure502 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-feedback
2. Restart container: docker compose restart docs-feedback
3. 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-db
2. Verify disk space: df -h
3. Restart container: docker compose restart feedback-db
4. Fastify automatically recovers once database is healthy.
curl -f http://127.0.0.1:8001/ready returns 200 OK.
C. Unplanned Host RebootAll 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 docker
2. 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 Crashdocker 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 docker
2. Start compose stack if not auto-started: docker compose up -d
Check container health with docker compose ps.
E. PostgreSQL Data CorruptionPostgreSQL 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 down
2. Remove corrupted volume: docker volume rm stackconsole-docs-feedback_feedback_pgdata
3. Recreate fresh volume & start DB: docker compose up -d feedback-db
4. Restore latest valid backup: ./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_LATEST.dump --force
5. Start API: docker compose up -d docs-feedback
Run post-deployment smoke test. Verify row count matches backup.
F. Accidental Database DeletionTable 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 --force
2. 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 DeploymentAPI 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-feedback
3. If frontend issue, toggle killswitch: DOCS_FEEDBACK_ENABLED=false
Verify /health and /ready probes.
H. SMTP OutageExternal 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 .env
2. Test SMTP reachability from host: nc -zv smtp.mailgun.org 587
3. Submissions remain safely stored in docs_feedback and can be audited via SQL.
Check application logs after fixing SMTP credentials.
I. Reverse Proxy OutageNginx 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 -t
2. Check upstream port 8001: curl -I http://127.0.0.1:8001/health
3. 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:

  1. Step 1: Execute Pre-Upgrade Backup:
    ./scripts/backup-feedback-db.sh
  2. Step 2: Pull Latest Version:
    git pull origin main
  3. Step 3: Run Test Suite Locally:
    npm test --prefix stackconsole-docs-feedback
  4. Step 4: Build Container Image & Apply Migrations:
    cd stackconsole-docs-feedback
    docker compose up -d --build
    Migrations execute automatically during startup.
  5. 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:

  1. Set docsFeedbackEnabled: false in docusaurus.config.ts or deploy frontend with DOCS_FEEDBACK_ENABLED=false.
  2. The DocFeedback widget disappears instantly across all documentation pages.

Tier 2: Backend Container Rollbackโ€‹

If the newly deployed container version contains an application bug:

  1. Check out the previous stable release tag:
    git checkout v1.0.0 # (or previous Git commit SHA)
  2. Rebuild and restart the container:
    cd stackconsole-docs-feedback
    docker compose up -d --build docs-feedback
    Note: Persistent database volume feedback_pgdata remains intact.

Tier 3: Database Point-in-Time Restoreโ€‹

If an errant schema change or data corruption occurred:

  1. Revert to the pre-upgrade backup created in Step 1:
    ./scripts/restore-feedback-db.sh backups/feedback/feedback_backup_PRE_UPGRADE.dump --force
Database Rollback Policy

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 KB payload 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/helmet applies 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 node in Dockerfile). 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-feedback Fastify container + Single-instance feedback-db PostgreSQL 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:

  1. 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.
  2. Centralized Logging & Telemetry:
    • Prometheus scrapers should aggregate /metrics across all running instances or scrape an ingress metric aggregator.
  3. 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โ€‹

  1. Health Verification:
    curl -s https://admindoc.stackconsole.io/health
    curl -s https://admindoc.stackconsole.io/ready
  2. Inspect Error Logs:
    docker compose logs --tail=100 docs-feedback | grep -i "error" || echo "Zero errors reported"
  3. Check Prometheus Metrics:
    curl -s http://127.0.0.1:8001/metrics | grep "docs_feedback_"

Weekly Maintenance Tasksโ€‹

  1. Verify Backup Generation: Confirm that nightly backup files are being generated and rotated in backups/feedback/:
    ls -lh backups/feedback/
  2. Check Container Disk Usage:
    docker system df
  3. 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:

  1. 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}';"
  2. Update .env in stackconsole-docs-feedback/:
    sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=${NEW_PASS}/" .env
  3. Restart the Fastify API container:
    docker compose up -d docs-feedback
  4. Verify readiness:
    curl -s http://localhost:8001/ready

19. Troubleshooting Matrixโ€‹

SymptomProbable CauseDiagnostic CommandResolution Procedure
GET /ready returns 503PostgreSQL container is starting or connection pool timeoutdocker compose logs feedback-dbCheck if PostgreSQL container is running. Verify POSTGRES_HOST, POSTGRES_USER, and POSTGRES_PASSWORD match in .env.
API Container Won't StartPort 8001 conflict or schema validation error in .envdocker compose logs docs-feedbackCheck port binding with ss -tuln | grep 8001. Fix malformed environment variables in .env.
PostgreSQL Won't StartFile permissions issue on volume or disk space exhausteddocker compose logs feedback-db and df -hEnsure host has free disk space. Check volume permissions.
Database Connection RefusedPostgreSQL not yet accepting connections or wrong hostdocker exec -t stackconsole-docs-feedback nc -zv feedback-db 5432Verify depends_on healthcheck condition in docker-compose.yml.
429 Too Many RequestsSubmitter exceeded 30 requests/minuteInspect /metrics counter docs_feedback_rate_limited_totalExpected behavior under spam prevention. Client must wait for Retry-After seconds.
CORS Policy Error in BrowserBrowser origin does not match DOCS_ALLOWED_ORIGINSBrowser DevTools ConsoleUpdate 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 offlinecurl -i http://127.0.0.1:8001/healthCheck Nginx reverse proxy logs (/var/log/nginx/error.log). Ensure location /api/v1/feedback is forwarded.
Feedback Submits Successfully But No Emails ArriveFEEDBACK_EMAIL_ENABLED=false or SMTP credentials invaliddocker 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 FailsContainer name mismatch or Docker daemon inaccessible./scripts/backup-feedback-db.sh outputEnsure container stackconsole-feedback-db is running. Check Docker permissions.
Restore Script FailsBackup file missing, corrupted, or incompatible formatfile <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 RestartRan docker compose down -v accidentallydocker volume ls | grep feedbackRestore database immediately from the latest .dump file using ./scripts/restore-feedback-db.sh.
Reverse Proxy Returns 502stackconsole-docs-feedback container is downdocker compose ps docs-feedbackRestart container: docker compose up -d docs-feedback.
Feedback Widget Not Visible on Docs PageFeature flag disabled in DocusaurusCheck siteConfig.customFields.docsFeedbackEnabledEnsure DOCS_FEEDBACK_ENABLED=true during npm run build.
Wrong Documentation Version RecordedCustom field currentVersion misconfiguredQuery 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:
    1. validate:
      • typecheck: Runs npm run typecheck to ensure strict TypeScript type safety.
      • dependency-audit: Runs npm audit --audit-level=high to detect vulnerable third-party dependencies.
    2. test:
      • unit-and-api-tests: Runs npm test executing Vitest/Jest unit and API route test suites.
    3. build:
      • docker-build: Uses docker:24.0.5-dind to build container images tagged with $CI_COMMIT_SHA and latest on main and release tags.
CI Validation vs. CD Deployment

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โ€‹

  1. Read Core Context: Inspect .ai/context/project.md, architecture.md, technology.md, and constraints.md.
  2. 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 5432 must never be exposed to public host interfaces.
  3. 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.
  4. Independent Validation: Changes must be validated in live execution environments (Docker, PostgreSQL, Docusaurus build, link validation) and recorded in .ai/reviews/.
Was this page helpful?
Found something that could be improved?