seedFromEmail(email) — Gravatar-compatible seed helper. Returns sha256 hex of the trimmed + lowercased email so the raw address never reaches URLs, server access logs, Referer headers, browser history, CDN cache keys, or analytics pixels. Two services hashing the same email produce the same seed → drop-in compatible with Gravatar's lookup scheme.
normalizeEmail(email) — exported canonicalization step (trim + lowercase + NFC) for callers who need to reproduce the form before hashing.
sha256Hex(input) — sync SHA-256 (FIPS 180-4) primitive used by seedFromEmail. Pure JS, no deps; available for callers that want to hash other inputs in the same scheme.
SeedOptions type + hashEmail option on Navii.seed().
Changed (@usenavii/core 0.7.0) — breaking
Navii.seed({ email }) now returns sha256(normalizeEmail(email)) instead of the raw address. Migration: if your existing avatars were keyed on raw emails and you need them to stay stable, pass { hashEmail: false } until you can re-key. New deployments should leave the default.
Navii.seedFromEmail exposed on the Navii namespace.
Added (@usenavii/react 0.8.0)
Re-exports seed, seedFromEmail, normalizeEmail, SeedFields, SeedOptions from @usenavii/core so <Navii seed={seedFromEmail(user.email)} /> works without a second import.
Added (API host)
/avatar/:seed sets x-navii-warning: plaintext-email-seed; hash with seedFromEmail() when the seed matches an email pattern. Render still succeeds — the header is a client-side nudge. Also logged at warn level for ops visibility.
Changed (API host)
/docs/recipes gets a new "Using emails as seeds (Gravatar-style)" section.
/docs/sdk-core#seed documents seedFromEmail, normalizeEmail, and the hashEmail option on Navii.seed().
/docs/http-api#headers documents the new x-navii-warning response header.
<NaviiGroup> — overlapping avatar stack, thin React wrapper around @usenavii/core's renderGroup(). Props: seeds, size, overlap, max (overflow → +N counter tile), ring, tileBg, counterFill, counterInk, plus all per-tile options (paletteId, palette, mood, background, animated, styleHint). <img> width is computed from size + overlap + max so layout is stable on load.
renderGroup + GroupOptions re-exported from @usenavii/react.
Changed (@usenavii/core)
No source changes. Stays at 0.6.0 — react 0.7.0 ships independently. Lockstep convention relaxed when only one package has source changes.
Changed (tooling)
scripts/release-audit.mjs — core/react version mismatch downgraded from error to warn. Lockstep stays the default expectation, but the audit no longer forces a no-op publish on the unchanged package.
Added (API host)
/docs/sdk-react documents <NaviiGroup> props + behavior, plus the new renderGroup/GroupOptions re-exports.
Per-release OG cards — GET /og/blog/v<X.Y.Z>.png composes a 1200×630 card for each minor+ release: dark radial background, hero avatar (deterministic from navii <version> seed + mood: happy, transparent so the mascot floats on the gradient), version pill, headline parsed from CHANGELOG, date, and navii.dev/blog brand mark. Cached per version. /blog/v<X.Y.Z> now sets og:image + twitter:image to this URL so social previews show the release-specific card instead of the generic landing OG image.
AvatarOptions.mood — new MoodId = 'neutral' | 'happy' | 'serious' | 'sleepy' | 'wink'. Overrides seed-derived eyes + mouth with a curated pair: happy → wide + smile, serious → squint + flat, sleepy → sleepy + dot, wink → wink + smirk. Same seed + same mood = byte-identical render. Different mood on the same seed shares body / palette / topper. Bypasses pack pick constraints by design (the mood IS the override). neutral (or undefined) preserves prior behavior.
Runtime palette injection in build() — options.palette (Palette object) now wins over spec.palette (id). Lets callers pass a brand or runtime-built palette without registering it in PALETTES. Fall-through: options.palette → spec.palette id → PALETTES[0].
React <Navii> forwards new mood and palette props through to the engine; MoodId re-exported from the React package.
Added (API host)
GET /avatar/:seed?mood=happy|serious|sleepy|wink|neutral — server-side mood overlay. PNG cache key extended with m= so moods don't collide.
Elorm UI logo (inline SVG, currentColor) in the landing "built with navii" wall, sized via new .logo svg.lg rule.
Pack overhaul — all 7 packs given distinct visual identities via new render flags (flat, bgColor, featureStroke, paletteExclusive, glow). Each pack now reads as a different illustration system, not just a recoloring.
New body shapes (pack-only, base seeds unchanged): squircle (full-bleed corporate tile), pumpkin, ghost, skullHead.
New mouth styles (pack-only): jagged (carved-pumpkin grin), fangs (vampire teeth).
New toppers: witchHat, pumpkinStem, ghostSheet, bob, bun, ponytail.
New accessory: earring (palette-themed stud + drop pair).
New outfit: tie (corporate necktie — knot + tapered blade).
Pack.glow flag emits an outer-glow SVG filter behind the body (Gaussian blur tinted by palette). Used by Neon.
AvatarSpec.flat / bgColor / featureStroke / glow — render directives the engine reads to alter body/face rendering per enabled pack.
featureStroke multiplier scales stroke widths on eyes, mouth, glasses uniformly. Office bumps to 1.35, Neon 1.5, Mono down to 1.15 (delicate).
New office-bright pack — vivid sibling of Office for marketing/design teams.
Changed (@usenavii/core 0.5.0)
Pack picks now authoritative — resolvePartPool no longer intersects against base pool, so packs can introduce ids unknown to the base (e.g. Office's squircle). Type system enforces validity.
Office, Halloween, Pastel, Neon, Mono, Earth packs reauthored with theme-cohesive picks, style hints, and exclusive palette pools.
Office uses full-bleed squircle + white plate + necktie outfits + bolder strokes for ID-badge look.
Halloween uses pumpkin/ghost/skullHead bodies + jagged/fangs mouths + witchHat/stem/sheet toppers + dark night plate.
Neon emits an outer glow halo via SVG filter behind the body.
Mono switched to full-bleed squircle (was contained orb) for editorial tile look.
Added (API host)
Polar.sh license verification — POST /license/verify now proxies to Polar's /v1/customer-portal/license-keys/validate (replaces Polar.sh). Validates status === 'granted', expiry, and optional benefit-id match.
GET /checkout — redirects to Polar checkout w/ configured product preselected. Powered by @polar-sh/hono.
POST /polar/webhooks — signature-verified webhook receiver, logs events.
New env vars: POLAR_ORGANIZATION_ID, POLAR_PRODUCT_ID, POLAR_BENEFIT_ID, POLAR_ACCESS_TOKEN, POLAR_SUCCESS_URL, POLAR_WEBHOOK_SECRET, POLAR_SERVER.
Compose file wires all Polar vars from /opt/navii/.env.
8 new license unit tests w/ fetch mock covering granted/revoked/expired/wrong-product/upstream-error paths.
Changed (API host)
Privacy page swapped Polar.sh references for Polar.sh.
AppOptions renamed Polar.shProductPermalink → polarOrganizationId + related Polar fields.
Added (Figma plugin)
Style hint pill row in Packs panel — Auto / Masc / Femme / Neutral toggle. Persisted via navii.style localStorage. Disabled when no pack active.
Plugin checkout URL now points at ${API_BASE}/checkout (instead of hardcoded Polar.sh link), letting us swap payment providers without re-publishing.
Fixed (Figma plugin)
Left column in Packs panel was not scrollable when content overflowed (e.g. with new Style hint section). Added overflow-y: auto + min-height: 0 to .col-left.
/privacy — public privacy policy page covering avatar requests, license verification, and analytics.
/support — public support page with contact email, GitHub issues link, and console-capture instructions.
Both pages linked from landing + docs footers and listed in /sitemap.xml.
Added (Figma plugin)
networkAccess.reasoning in manifest.json explaining why api.navii.dev and navii.dev are allowlisted.
Offline pre-flight on insert, fill-random, and license-verify — surfaces a clear notification instead of failing silently when navigator.onLine is false.
notify message type so the UI iframe can push toasts through figma.notify.