greenpng Deployment & Operations Guide

Complete reference for deploying the greenpng probe plane, embedding tamper-evident browser collectors, configuring multi-tenant sites, and querying risk verdicts via backend SDKs.

# 1. Quick Start & Server Installation

The greenpng server is distributed as a self-contained, statically compiled Rust binary bundle with an embedded Pingora edge gateway, administration console, and automated health checks.

๐Ÿ’ก System Requirements

Linux x86_64 or aarch64 (Ubuntu 20.04+, Debian 11+, RHEL/Rocky 8+), systemd. Ports: 28680 (admin console & control-plane API), 28765 (probe plane โ€” /gr.js, /dist, /v1), 28766 (gateway, reserved).

Automated Installation Script

Run the official installation script with automated SHA-256 and Ed25519 signature verification:

# Download and execute the official installer
curl -fsSL https://raw.githubusercontent.com/greenpng/gr-server/main/install/install.sh | bash

# Or pin an explicit version and architecture:
bash install/install.sh --version 1.0.14 --arch x86_64 --yes

The installer creates the isolated greenpng:greenpng system user, places binaries in /opt/greenpng/bin, configures the systemd unit greenpng.service, and sets up automated maintenance timers.

# Optional Containerized Data Layer

greenpng itself always runs as a host binary supervised by systemd. Containers are only used for the data layer: pass --with-docker and the installer brings up PostgreSQL and Redis via install/data-compose.yml:

# Automated install with a containerized data layer (PostgreSQL + Redis only)
bash install/install.sh --version 1.0.14 --with-docker --yes

# Inspect / stop the data layer (database volumes are never touched by uninstall)
docker compose -f install/data-compose.yml ps
docker compose -f install/data-compose.yml down    # add -v to also drop volumes
โ„น๏ธ Host binary by design

There is no greenpng application container image: the probe plane terminates TLS and serves sealed ingest as a native systemd service for predictable resource control and self-OTA restarts.

# Admin Bootstrap Credentials

Upon the first startup, greenpng creates an initial administrator login and a randomized console path:

# View the one-time generated admin credentials
cat /opt/greenpng/data/admin/admin_bootstrap_once.txt

The file (mode 0600) contains your randomly designated console path (e.g. http://your-server:28680/<random-path>/) and one-time initial credentials in plain text โ€” rotate them after the first login.

# 2. Browser Probe Deployment Modes

The greenpng browser probe is modular, cryptographically signed, and designed to gather telemetry without triggering browser permission prompts. Production deployments separate two first-party domains: a pv domain (script + session lifecycle: /gr.js, /dist, /v1) and a gv domain (sealed upload: /v1/ingest/sealed). Browser uploads always go directly to the bound gv domain over TLS โ€” only script loading may be reverse proxied.

Mode A: Direct Script Embedding

Include the bootstrap loader in your website HTML template before the closing </body> tag:

<script
  src="https://pv.yourdomain.com/gr.js"
  data-site-id="site_prod_90b21e"
  data-endpoint="https://pv.yourdomain.com"
  defer>
</script>

Attributes: data-site-id (required, from the admin panel), data-endpoint (pv base URL; defaults to same-origin under the inject path), data-inject-path (custom /gr.js mount path), data-gw-base (override for the gv upload base โ€” defaults to the bound gv domain issued with the session grant).

Mode D: Application Embedding

Render the same tag from your server templates (Twig / Jinja / EJS / Thymeleaf) instead of editing static HTML โ€” identical attributes, injected just before </body>.

# Mode B: Nginx First-Party Relay (Recommended)

To eliminate cross-domain CORS preflights and make probe packets invisible to third-party ad-blockers, reverse-proxy the pv paths through your own domain. The gv upload path is not proxied โ€” the browser uploads direct to the bound gv domain:

# pv.yourdomain.com โ€” first-party proxy to the probe plane (28765)
location = /gr.js {
    proxy_pass http://127.0.0.1:28765/gr.js;
    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;
    add_header Cache-Control "no-store" always;   # carries the current fe_epoch
}

location /dist/ {
    proxy_pass http://127.0.0.1:28765/dist/;
    proxy_set_header Host $host;
    add_header Cache-Control "public, max-age=31536000, immutable" always;  # content-hashed
}

location /v1/ {
    proxy_pass http://127.0.0.1:28765/v1/;
    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;
    client_max_body_size 1m;
}
โ„น๏ธ Cookie Forwarding

With the first-party proxy, the browser naturally attaches your site cookies to the open and ingest requests, allowing server-side cookie capture (ยง3) without touching sensitive credentials in client JavaScript.

๐Ÿ” Trusted proxies & real IPs

Add your proxy CIDRs to GR_TRUSTED_PROXIES in /opt/greenpng/.env so rate limits and telemetry key on the real visitor IP. Behind Cloudflare, restore it with set_real_ip_from + real_ip_header CF-Connecting-IP.

# Mode C: Cloudflare Worker Edge Injection

Inject the probe dynamically on HTML page responses at the Cloudflare Edge network:

export default {
  async fetch(request) {
    const response = await fetch(request);
    const contentType = response.headers.get("content-type") || "";

    if (contentType.includes("text/html")) {
      return new HTMLRewriter()
        .on("head", {
          element(el) {
            el.append(
              '<script src="/gr.js" data-site-id="site_prod_90b21e" defer></script>',
              { html: true }
            );
          },
        })
        .transform(response);
    }
    return response;
  },
};
โœ… Deployment recommendations

Cookie allowlist first โ€” without business cookies in the site allowlist the probe cannot bind sessions to business identities; configure it before going live.

Caching โ€” /gr.js must stay no-store (it carries the current fe_epoch); /dist/* is content-hashed, cache it immutable; never cache /v1/*.

Smoke test โ€” after wiring, curl -s https://pv.yourdomain.com/v1/health must return 200; then open a real browser page and confirm the session appears in the admin panel.

# 3. Backend SDKs & API Contract

Once a visitor lands on your page, greenpng evaluates their telemetry. Your backend queries the final verdict via REST API or six native SDKs (Go, Rust, TypeScript, Python, PHP, shell) using the site-scoped key created in the admin panel:

GET /v1/session/{sessionId}/result?projection=sdk
Host: pv.yourdomain.com
X-Gr-Sdk-Key: grsk_โ€ฆ

Sample JSON Verdict Response (projection=sdk)

{
  "ok": true,
  "schema_version": "product_public_v1",
  "session_id": "sess_91bf20a4ce",
  "projection": "sdk",
  "product_public": {
    "bot": {
      "result": "not_detected",       // "not_detected" | "good" | "bad"
      "verdict": "human",             // "human" | "watch" | "bot" | "crawler" ...
      "flags": [],
      "score": 0.02
    }
  },
  "sdk_projection": {
    "algo": "sdk_slim_projection_v1",
    "device_id": "dev_9f48a1c8e0324b91b72a9df0",
    "cookie_fields": { "user_id": "u_884920" },
    "os":  { "score": 0.99, "status": "real", "coverage": 0.92 },
    "br":  { "score": 0.97, "status": "real", "coverage": 0.88 },
    "rpa": { "score": 0.02, "status": "real", "coverage": 0.75 },
    "privacy": { "silent_probe_no_permission_request": true }
  },
  "note": "sdk_projection is the slim SDK contract"
}

# Result Projections

The projection query parameter controls the granularity of returned data:

  • public: merchant schema only โ€” safe for external display.
  • sdk (default): slim SDK contract โ€” verdict block, per-axis scores (os / br / rpa), stable device ID, and captured allowlisted cookies.
  • diagnostic: full forensic breakdown. Requires an ops/admin token โ€” site keys are refused by the server.

# 4. Admin Console Portal

The console lives on the control plane at http://<server>:28680/<random-path>/ โ€” the randomized path shields the login endpoint from automated brute-force scans.

Site management, SDK key issuance, and the Config page (rate limits, hot/cold tiering, robot fastlane) are edited in the panel; publishing applies immediately on the publishing node and propagates to cluster nodes within ~30 seconds without restart.

๐Ÿšฆ Rate-limit defaults (v1.0.14)

Site totals default to 0 (unlimited) โ€” an aggregate cap can throttle real users mixed into bot floods. Client telemetry is bounded per single IP instead: rate_limit_client_event_per_ip_per_min = 100; exceeding it caps only that IP.

# OTA Updates & Rollbacks

Three channels, in priority order: P0 panel OTA (Set Release URL โ†’ Install Runtime / FE โ€” zero downtime for modules and frontend), P1 CLI updater /opt/greenpng/install/release/update_runtime_from_github.sh, P2 the unattended greenpng-auto-upgrade.timer.

Every update pulls the signed release bundle of the same tag, verifies the Ed25519 signature, stages the files, performs an atomic swap, restarts the service, and probes /v1/health eight consecutive times. On failure an immediate automated rollback restores the previous binary, frontend tree, and version stamp.

# FAQ & Troubleshooting

Q: Why am I receiving HTTP 403 on mutating API calls?

greenpng verifies the Origin (or Referer) of mutating calls against the site's bound domains and rejects mismatches โ€” and non-browser clients that send no Origin at all must authenticate differently. If you front the server with a proxy, pass the original Host and X-Forwarded-* headers through unchanged. Since v1.0.13 an http/https scheme mismatch alone only logs a WARN and is accepted.

Q: Does greenpng store PII or violate GDPR?

No. greenpng is architected with Privacy-by-Design. Incoming visitor IPs are truncated to /24 (IPv4) or /48 (IPv6) subnets before persistence, and device IDs are derived via irreversible cryptographic hashes of non-personal hardware attributes.

Q: How do I perform an OTA upgrade?

Prefer the panel OTA (P0). Or run /opt/greenpng/install/release/update_runtime_from_github.sh: it fetches the latest signed release bundle, checks the Ed25519 signature, stages the files, performs an atomic swap, restarts the service, and verifies /v1/health before finalizing โ€” with automatic rollback on failure.