Badge

Pages served by CloudGrid carry a small corner badge: "Running on Cloud Grid". It identifies the platform and collects feedback from viewers. This page is the contract for how the badge is delivered, what it does, and how to control or theme it.

1. Delivery

The badge is a standalone script served at https://api.cloudgrid.io/badge.js. The platform injects a small <script src="…"> tag before </body> on served HTML — for Inspirations at serve time, for Apps at the ingress layer. The script bytes are never inlined into your page, so the browser caches the bundle once across all entities.

Cache headers: Cache-Control: public, max-age=86400, must-revalidate — bundle updates roll out within a day.

2. The injected script tag

The injected tag carries these attributes:

AttributeRequiredValue
srcyeshttps://api.cloudgrid.io/badge.js
data-cloudgrid-tagyesTag identity slug — fixed "badge" for this contract
data-cloudgrid-badgeyes (legacy compat)Presence flag
data-cloudgrid-entityyesEntity UUID
data-cloudgrid-orgyesGrid slug (e.g. acme), not a UUID
data-cloudgrid-badge-modeyes (when emitted)auto (default — respects a session dismiss), force-show (platform-forced), locked-show (grid-forced)
data-cloudgrid-positionnobottom-right (default), bottom-left, top-right, top-left
data-cloudgrid-themenoJSON object — theme keys from the whitelist below; unknown keys are dropped

When the resolved badge state is off, the platform suppresses the snippet entirely — no hidden tag is emitted.

3. Behavior — three states

Idle

Expanded (on click)

A slide-over panel (about 360px wide on desktop; bottom-sheet on mobile) with three sections:

Confirmed (after a feedback submit)

Trigger semantics

The panel opens only on click. No auto-popup, no hover trigger, no timed flash.

4. Theming

All badge styling reads CSS variables with these defaults, inside a closed Shadow Root — your page's CSS never leaks into the badge, and the badge never leaks into your page:

--cg-badge-bg: #1f2937;
--cg-badge-fg: #f9fafb;
--cg-badge-accent: #3b82f6;
--cg-badge-radius: 6px;
--cg-badge-shadow: 0 1px 3px rgba(0,0,0,0.12);
--cg-badge-font: system-ui, sans-serif;

A grid-level theme, when set, rides in on data-cloudgrid-theme as a JSON object whose keys are the bare names (bg, fg, accent, radius, shadow, font). Keys outside the whitelist are dropped; values containing ;, {, }, or </ are rejected. Position is set per entity via data-cloudgrid-position; default bottom-right.

Theme editing UI is not shipped yet — the schema is ready and the badge already applies a theme when one is set.

5. Turning the badge on and off — precedence

Three levels, resolved top-down. Platform beats grid; grid beats entity:

1. platform policy 'force_on'   → badge ON (everything else ignored)
2. platform policy 'force_off'  → badge OFF
3. grid policy 'force_on'       → badge ON (entity setting ignored)
4. grid policy 'force_off'      → badge OFF
5. entity badge_mode 'on'       → badge ON
6. entity badge_mode 'off'      → badge OFF
7. (none of the above)          → ON (the default)

Setting badge_mode on an entity (PATCH /api/v2/entities/<id>) when a grid or platform policy would override it returns 403 BADGE_POLICY_OVERRIDE. The message names the source (platform policy or your grid) and the direction (forces it on / forces it off), so you know whom to contact. The request is refused up front — it is not silently accepted and ignored.

6. Feedback endpoint

POST https://api.cloudgrid.io/api/v2/entities/<entity_id>/feedback

Headers:
  Content-Type: application/json
  (no Authorization — feedback is unauthenticated; rate-limited per IP + entity)

Body:
  {
    "category": "reaction" | "bug" | "feature_request" | "comment" | "other",
    "text": string (max 2000 chars),
    "source": "badge",
    "user_agent": string (optional),
    "page_url": string (optional)
  }

Response 200:
  { "ok": true }

Response 4xx:
  { "error": string, "code": string }

7. Idempotency

The bundle exposes one global symbol: window.__CloudGridBadge. Loading the script twice is a no-op — the first init wins.

8. Not in this version