Browse documentation

🧭 Start Here

Blackcap Overview ✨ Blackcap Feature Catalog Installation First Run

🚀 Deploy Blackcap

Platform Stacks and Raspberry Pi Hardware Raspberry Pi Deployment Raspberry Pi Client Services GCP Deployment Application Updates Environment Variables and Secrets Reverse Proxy and TLS Background Jobs and Schedules

🛠️ Administer Blackcap

Organizations Users, Permissions, and Authentication Configuration Workspace Backups and Restore Database Administration Regression Testing Performance and Job Status Audit, Access Activity, and Logging GeoIP and Access Location Data Retention and Purge Support Requests API Tester and Postman Instance Reporting

🍽️ Use Recipes

Recipes and the Recipe Library Recipe Import and Discovery Recipe Editing and Cache Artifacts Recipe Sharing Social Recipe Import AI Recipe Image Generation

📅 Plan Meals

Meal Planner

🛒 Use Shopping Lists

Shopping Lists and Shop a List External and Household Shopping 🧩 Chrome Extension Shop With

🧺 Manage Kitchen Inventory

🧺 Kitchen Inventory

🖥️ Use Displays

Displays and Connections Assigning and Scheduling Display Content Remote Pi Client E-Ink Rendering Menu Refresh and Rendering Noun Project Footer Images

🧑‍🍳 Cook with Let’s Cook

🧑‍🍳 Let’s Cook 🧑‍🍳 Let’s Cook Controls and Timers

🤖 Use and Administer AI

🤖 AI in Blackcap 🤖 AI Providers and Connections 🤖 AI Seeds and Usage

🧩 Use the Chrome Extension

🧩 Blackcap Chrome Extension 🧩 Chrome Extension Recipe Capture 🧩 Chrome Extension Shop With 🧩 Chrome Extension Release and Privacy

🎮 Play Games

🎮 Games and Trivia

🔌 Integrations

Email Integration Cloud Storage Integrations Voice Assistants Shop With Integrations Authentication Providers

⚙️ Develop Blackcap

Application Architecture Database Service and Data Access SQLite and PostgreSQL Database Migrations Background Job Architecture Testing API Architecture Security and Organization Scoping UI, Icons, and Documentation Assets Blackcap-Safe Emoji Documentation Standards Terminology

⚠️ Troubleshoot Blackcap

⚠️ Troubleshooting Deployment Troubleshooting Display Troubleshooting Recipe Import Troubleshooting 🤖 AI Troubleshooting Backup Troubleshooting Database Troubleshooting Diagnostic Organization Clones Support Requests

Audit, Access Activity, and Logging

Audience: System Admin, Org Admin, Support, Deployment Administrator Related: Data Retention · Instance Reporting · Environment And Secrets

Blackcap records authentication, configuration, recipe, meal-planning, shopping, backup, display, AI, and administrative activity according to the feature and retention policy. Access activity may include IP-derived country details for diagnostics; presentation of country flags is configurable without disabling the underlying activity record.

Application logs are available through systemd journal output. User-facing activity should remain understandable and privacy-conscious; raw secrets, provider credentials, and unnecessary request data must not be logged.

This note documents the implemented foundation for access-source tracking and Pi/non-Pi branding.

Testing Checklist

  • Direct IP, trusted proxy IP, spoofed untrusted header, IPv4, IPv6, and private/local IP capture.
  • GeoIP disabled fallback, missing DB fallback, private/LAN handling, and lookup failure handling.
  • Internal/external login success/failure activity rows where existing audit events are emitted, including MFA-completed login flows.
  • Display receiver, kiosk, display state, remote client state/heartbeat/preview access-source updates.
  • Repeated same-IP display polling updates one row rather than creating many rows.
  • Display list up-to-five country/LAN pills with real countries first, SVG/emoji/code fallback, all-country tooltip, and display detail access-source table.
  • Activity log and Account page country/location indicator and tooltip.
  • Scheduled purge removes access-source rows and GeoIP cache entries older than retention.
  • Runtime branding shows Blackcap Pi Admin only when Raspberry Pi is detected or explicitly overridden.
  • Raspberry Pi Client feature terminology remains unchanged.

stunnel/Gunicorn PROXY protocol and HTTPS enforcement

With stunnel terminating TLS and Gunicorn receiving PROXY protocol, Blackcap can now see the real client IP. That also means request.remote_addr is no longer 127.0.0.1, while Flask still sees the backend hop as plain HTTP. Without a proxy-aware HTTPS setting, security.enforce_https_external can incorrectly block the external login page with HTTPS is required.

Migration 128_trust_external_https_base_url.sql adds DB-backed platform setting:

security.trust_external_base_url_https = true

When enabled, Blackcap treats the request as secure only when auth.external_base_url is HTTPS and the current request Host matches that configured external URL. This is intended for stunnel/Gunicorn PROXY protocol deployments. Keep Gunicorn bound to 127.0.0.1 so direct external HTTP cannot bypass stunnel. Reverse proxy deployments that set X-Forwarded-Proto should instead enable security.trusted_proxy_headers_enabled and configure security.trusted_proxy_cidrs for the immediate local proxy.

DB-backed GeoIP setup

The active GeoIP settings are platform-scoped DB settings:

geoip.enabled = true
geoip.provider = maxmind
geoip.maxmind_database_path = /home/pi/Blackcap-Pi/data/geoip/dbip-country-lite.mmdb

The provider = maxmind value means “use the local MaxMind-compatible MMDB reader.” It can point at a MaxMind GeoLite2 Country database or a DB-IP Lite Country .mmdb file. The current implementation does not call a MaxMind network API.

Install the Python dependency through the normal requirements file:

cd /home/pi/Blackcap-Pi
/home/pi/inky_env/bin/python3 -m pip install -r requirements.txt

To install only the GeoIP reader dependency:

/home/pi/inky_env/bin/python3 -m pip install maxminddb==2.8.2

For MaxMind GeoLite2 Country, configure /etc/GeoIP.conf with your MaxMind AccountID, LicenseKey, and EditionIDs GeoLite2-Country, then run either sudo geoipupdate -v or the bundled helper. These MaxMind credentials are required only for MaxMind/geoipupdate downloads; they are not required when Blackcap is pointed at a manually downloaded DB-IP Lite or other compatible MMDB file:

sudo /home/pi/inky_env/bin/python3 /home/pi/Blackcap-Pi/tools/blackcap_geoip_update.py   --config /home/pi/Blackcap-Pi/inky_menu_config.ini   --geoip-conf /etc/GeoIP.conf   --database-dir /usr/share/GeoIP   --verbose

tools/blackcap_geoip_update.py shells out to geoipupdate and then clears Blackcap's ip_geo_cache through the DB service/repository layer so cached IPs can be re-resolved against the updated MMDB. Configuration → Platform → GeoIP / Access Location also includes an Update GeoIP DB Now button in the Status section. The button runs the same update helper from the Blackcap process and reports setup/permission errors instead of refreshing the page. The preferred unattended delivery is still the bundled systemd timer because it runs as root by default and can write /usr/share/GeoIP:

sudo cp /home/pi/Blackcap-Pi/systemd/blackcap_geoip_update.service /etc/systemd/system/
sudo cp /home/pi/Blackcap-Pi/systemd/blackcap_geoip_update.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now blackcap_geoip_update.timer

The sample timer is monthly with Persistent=true and a randomized delay. That matches DB-IP Lite's monthly cadence. DB-IP Lite does not need a MaxMind account. MaxMind-managed downloads are still supported by setting geoip.update_source = maxmind_geoipupdate and configuring /etc/GeoIP.conf; weekly is also reasonable for that mode if fresher data is desired. Dates on platform configuration pages and GeoIP diagnostics are displayed in the Default organization time zone.

Preferred configuration path:

  1. Install or refresh Python dependencies so maxminddb is present in the Blackcap virtual environment.
  2. Install a local .mmdb country database on the Blackcap host, normally DB-IP Lite Country at /home/pi/Blackcap-Pi/data/geoip/dbip-country-lite.mmdb on the Pi or /var/lib/blackcap/geoip/dbip-country-lite.mmdb on GCP, or MaxMind GeoLite2 Country at /usr/share/GeoIP/GeoLite2-Country.mmdb.
  3. Let migration 127_geoip_maxmind_db_defaults.sql seed the DB-backed platform settings, or adjust the geoip.* values in Configuration → Platform → Security if using a different compatible MMDB path.
  4. Restart Blackcap.
  5. Trigger a fresh access request; existing Unknown rows are updated on the next matching access-source upsert, while unrelated historical Unknown rows remain until purged or seen again.

Bundled country flag SVGs live in inky_admin/static/country_flags/. Configuration → Platform → Country Flags can generate missing observed-country assets or the broader MaxMind country-code asset set when the optional OS tools are installed. Additional assets can also be created from observed MaxMind/GeoIP country codes with tools/generate_country_flag_assets.py --from-access-activity --config /home/pi/Blackcap-Pi/inky_menu_config.ini, from the ISO country-code set normally returned by MaxMind country lookups with tools/generate_country_flag_assets.py --from-maxmind-country-codes, or from all available TeX Live worldflags two-letter codes with tools/generate_country_flag_assets.py --codes all. The generation tool is optional and requires OS packages (texlive-latex-extra for worldflags/pdflatex, plus inkscape); runtime Blackcap only needs the generated .svg files.

Service-layer setup/check command for headless installs:

cd /home/pi/Blackcap-Pi
INKY_CONFIG_PATH=/home/pi/Blackcap-Pi/inky_menu_config.ini /home/pi/inky_env/bin/python - <<'PY'
from inky_admin.services.config_service import load_config
from inky_admin.services.database_service import app_db_transaction, init_app_db
from inky_admin.services.settings_service import set_setting, get_config_bool, get_config_str
from inky_admin.services.geoip_service import lookup_ip

init_app_db()
config = load_config()
with app_db_transaction(config) as conn:
    set_setting(conn, "geoip.enabled", "true", config=config, scope_type="platform", scope_id=0, source="manual_geoip_setup", actor="admin")
    set_setting(conn, "geoip.provider", "maxmind", config=config, scope_type="platform", scope_id=0, source="manual_geoip_setup", actor="admin")
    set_setting(conn, "geoip.maxmind_database_path", "/home/pi/Blackcap-Pi/data/geoip/dbip-country-lite.mmdb", config=config, scope_type="platform", scope_id=0, source="manual_geoip_setup", actor="admin")

print("geoip.enabled =", get_config_bool(config, "geoip", "enabled", False))
print("geoip.provider =", get_config_str(config, "geoip", "provider", "none"))
print("geoip.maxmind_database_path =", get_config_str(config, "geoip", "maxmind_database_path", ""))
print(lookup_ip("8.8.8.8", use_cache=False).as_dict())
PY

Local tunnel / reverse proxy diagnostic

If display access activity only shows 127.0.0.1 or ::1, Blackcap is usually seeing the local TLS tunnel or reverse proxy connection rather than the original browser/display client. The display detail Access Activity card now surfaces a warning in that case so the data is not over-interpreted as a meaningful country/IP breadth signal.

Real client IP capture requires the upstream HTTPS/reverse-proxy layer to preserve the original client address, either by setting trusted HTTP forwarding headers such as X-Forwarded-For / X-Real-IP, or by using PROXY protocol where the receiving server supports it. Blackcap should only trust those values when security.trusted_proxy_headers_enabled is on and security.trusted_proxy_cidrs contains the local proxy/load-balancer address.

Current viewer location feedback

The login page, hosted display receiver, and kiosk page show a compact current-request country indicator for the person viewing the page. This is intentionally presentation-only: it uses the same IP detection and local GeoIP lookup path, but it does not add extra activity rows beyond the normal login/display/API access tracking. Tooltips use friendly wording such as “I see you are logging in from India.”

API token access tracking

User-owned API token usage is recorded in access activity through the same unique-IP/country breadth model as display and login access. Valid token usage is scoped to the token organization. Invalid token attempts are tracked against the Default organization when no token owner can be identified. Legacy admin-token fallback requests are recorded separately as admin-token API access.

On this page