---
description: Ship-safe rules - secrets, security headers, HTTPS, asset paths, bundle size
alwaysApply: true
---
# Ship-safe rules (always on)
Source: https://agentcartridge.com/rules/ship-safe (free; updated 2026-10-19)
Apply these whenever you touch config, env vars, auth, routes, assets, dependencies or deployment.

## 1. Secrets in the client or in the repo
- Never put API keys, tokens, database URLs or service-role keys in client-side code, or in any env var with a public prefix (NEXT_PUBLIC_, VITE_, PUBLIC_, EXPO_PUBLIC_, REACT_APP_). Those values are inlined into the browser bundle.
- Only publishable values get a public prefix (analytics IDs, Stripe publishable key, Supabase anon key with RLS on).
- Never commit .env files. Before creating one, make sure .gitignore lists .env, .env.local and .env*.local. Keep a .env.example with placeholder values only.
- Calls that need a secret go through a server route, server action or edge function; the browser only talks to that route.
- If you find a secret in client code or git history, stop and tell me so I can rotate it. Deleting the line is not enough.

## 2. Missing security headers
- Every web app sends these response headers in production:
  - Content-Security-Policy: start strict (default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none') and allow only the extra origins the app really uses.
  - Strict-Transport-Security: max-age=63072000; includeSubDomains (many hosts, e.g. Vercel, already send HSTS: check before adding a second one).
  - X-Content-Type-Options: nosniff
  - Referrer-Policy: strict-origin-when-cross-origin
  - X-Frame-Options: DENY (older browsers), unless the app must be embedded.
- Set them in one place in the framework or host config (next.config headers(), vercel.json "headers", Netlify _headers), not per page. frame-ancestors only works as a real header, not in a <meta> tag.
- When adding a third-party script, widget or API, add that exact origin to the CSP. Never "fix" a CSP error by removing the CSP or adding *.
- After changing headers, load the app and check the browser console for CSP violations.

## 3. Weak HTTPS assumptions
- Never hard-code http:// URLs for your own site, APIs or assets. Use https:// or relative URLs, and read the base URL from config.
- Session and auth cookies are Secure, HttpOnly and SameSite=Lax (or Strict).
- Redirect HTTP to HTTPS at the host, and never load scripts, styles or API calls over plain HTTP (mixed content).
- Webhook and OAuth callback URLs are https:// in production.

## 4. Broken asset and link paths
- Reference public assets one consistent way. On a site served from the domain root, use root-relative paths (/images/logo.png). If the app is served from a subfolder (GitHub Pages project sites, itch.io), set the framework's base path (e.g. Vite base: "./") instead of hand-editing URLs.
- Never reference local file system paths (C:\..., /Users/...) or files outside the public folder.
- File names are case-sensitive on most hosts: match the exact case.
- After adding, renaming or removing a route, update every internal link, redirect and the sitemap. No links to routes that don't exist.
- Images always have width and height (or an aspect ratio) and meaningful alt text.

## 5. Fat client bundles
- Don't import a whole library for one function (lodash, moment, full icon packs). Import the specific module or use the platform (Intl, Array methods, fetch).
- Keep data fetching and heavy logic on the server when the framework supports it (server components, loaders, API routes).
- Lazy-load below-the-fold components, charts, editors and maps.
- Ask before adding a dependency, and say what it adds.
- Before finishing a feature, check the build output for any client chunk that grew a lot and explain why.
