Dev command center, macOS Keychain integration, and proactive Xero token refresh

2026-04-06

A collection of developer-experience improvements that I kept wanting for weeks and finally bundled into a single PR. None of them are flashy; all of them remove daily friction.

/dev/ — command center for every service

A new route at /dev/ that renders a grid of quick links to every external service the project touches: PostHog, Vercel, Xero, GitHub, Stripe, plus placeholders for six more services I'm planning to plug in (SendGrid, Linear, Fly.io, Cloudflare, Stripe Connect, 1Password).

  • Dev-mode only. The route is registered conditionally on PORTAL_DEV_MODE=1, and middleware redirects any request under /dev/* away in production, independently of the env var, as a second layer of safety.
  • Renders as a simple dashboard card grid — each card has a name, a one-line description of what the service does for the project, and a button that opens the relevant dashboard in a new tab.
  • Saves me the "which bookmark folder was Xero in again" problem about fifteen times a day.

macOS Keychain as a secrets fallback

Previously: every secret lived in .env, and I had to remember to keep .env in sync across the Mac mini and my laptop. Error-prone.

Now: honest-cam reads secrets in this order:

  1. Environment variable.
  2. .env file.
  3. macOS Keychain via the security CLI.

For the Mac mini and my laptop, the Keychain layer means there's a canonical, encrypted, OS-managed place each machine reads from, and .env becomes strictly for dev-only override values. Covered secrets: Anthropic API key, Xero client ID and secret, Stripe keys.

The Keychain read is wrapped in a small helper that silently falls through if the key isn't set — so the same code runs unmodified on Linux or in CI, where Keychain doesn't exist and the env var / .env path handles everything.

Proactive Xero token refresh

The Xero OAuth bug that kept biting me: refresh tokens expire 60 days after last use. If I didn't run a Xero command for two months (easy during heads-down feature work), the next run would hit a 401 and I'd have to re-authorize through the web UI.

Fix: every honestcam xero-* command now proactively refreshes the token at startup, regardless of whether the current access token is about to expire. The refresh itself counts as "use" and resets the 60-day clock. The tradeoff is one extra HTTP call per command invocation, which is fine at this scale.

A small status message fires on refresh so I can see it happened: Xero token refreshed (expires in 30m, refresh window reset to 60d).

Landing page redesign (rolled in)

PR #12 landed the first cut of the GoHighLevel-inspired redesign. This PR carries forward some follow-on tweaks that came out of the live review: tightened hero spacing on mobile, staggered animation timing, and a couple of typographic nits in the dark navy sections.

Vendor mapping fixes

Three new real-world vendor aliases added to the Xero sync mapping that Bamboo House's bills uncovered:

  • Waste Management — variations including "WM MIAMI", "Waste Management Inc.", "W.M. of Florida".
  • Nobus Group II — property inspection contractor, three filename variants.
  • Neighborhood Compliance Services — code enforcement consulting, two variants.

Each one adds rows to the vendor alias YAML; the resolver picks them up automatically on the next sync.

OG screenshots retaken

The OG preview images on the landing pages were showing the pre-redesign layout. Retook them at 1200×630 via Playwright, now matching the current live pages. Every landing page's social preview reflects the new design.

Docs

  • docs/feature-landing-pages.md — added the update process checklist (carried over from PR #13).
  • docs/feature-vendor-portal.md — notes on the next vendor-facing feature.
  • docs/memory/… — session notes from the review that produced this bundle.

Test plan

  • Verify all four landing pages on desktop and mobile.
  • honestcam xero-sync bamboo-house --dry-run — confirm Keychain fallback kicks in when .env is absent.
  • Confirm the "token refreshed" message appears on the next Xero command.
  • Verify /dev/ is reachable when PORTAL_DEV_MODE=1 and redirects without it.
  • Re-check OG previews via the Facebook Sharing Debugger.

PR: https://github.com/StevieIsmagic/honest-cam/pull/14