A fast content calendar that runs live on top of Linear.
We plan a lot of content at ngrok, and our issues already live in Linear with due dates, labels, and assignees. What we didn't have was a calendar that felt good to use: the tool we'd been living in was slow and regularly froze the browser tab. calgrok is the rebuild. It reads everything straight from the Linear API, shows your content issues on a month grid by their due date, and lets you reschedule with a drag or a click. It stores nothing of its own.
Built for how ngrok's GTM and Content teams work, but configurable enough to run against another Linear workspace.
- Shows Linear issues on a month calendar, placed by due date (which we treat as the publish date).
- Drag an issue to another day to reschedule it, with the change saved to Linear optimistically (it moves instantly and rolls back if Linear says no).
- Opens any issue in a modal to read the description and edit its due date or tags, including clearing the date to drop it off the calendar.
- Discovers teams from Linear after login, filters by explicit team selection and label, and remembers view options (show weekends, completed, and sub-tasks) between visits.
calgrok is a React Router app that talks to Linear over OAuth. The token exchange and every Linear request happen on the server, so your access token lives in a signed, httpOnly cookie and never reaches the browser. There's no database: the server is a thin proxy in front of Linear, and the browser caches what it fetches for the session with TanStack Query. The UI is built with mantle, ngrok's design system.
You'll need your own Linear OAuth app and a Linear workspace to point it at.
- Create an OAuth app at
linear.app/settings/api/applications/new.
Add
http://localhost:3000/auth/linear/callbackas a redirect URL (add your deployed URL's callback too, once you have one), and request thereadandwritescopes. - Copy
.env.exampleto.envand fill inLINEAR_CLIENT_ID,LINEAR_CLIENT_SECRET,LINEAR_REDIRECT_URI, and aSESSION_SECRET. Optionally setLINEAR_LABEL_NAMESPACEif you want to scope the calendar to a label namespace such ascon/. - Install and run:
pnpm install pnpm dev # http://localhost:3000 - To deploy, build the container and supply the same environment variables at
runtime:
Serve it over HTTPS (the session cookie is
docker build -t calgrok . docker run --rm -p 3000:3000 --env-file .env calgrokSecurein production) and make sure the deployed callback URL is registered on your Linear OAuth app.
calgrok discovers Linear teams after login. Select the teams you want in the UI; that selection is stored in your browser and no issues are fetched until at least one team has been selected.
By default, LINEAR_LABEL_NAMESPACE is blank, so the calendar shows dated
issues from the selected teams and labels are optional filters. If you set
LINEAR_LABEL_NAMESPACE=con/, only labels with that prefix are offered, the
prefix is stripped in the UI, and unfiltered calendar views are scoped to issues
with one of those labels.
.
├── app/
│ ├── routes/ Pages, the OAuth flow, and the BFF API routes that proxy Linear.
│ ├── features/
│ │ └── calendar/ The month grid, issue cards, filters, modal, and data hooks.
│ └── lib/ Server-side env, session, and Linear client helpers.
├── Dockerfile Multi-stage production build.
└── PLAN.md The design and the milestone-by-milestone build log.
This is an internal ngrok tool we're sharing as-is. It's shaped around how we work and isn't a supported product, so we may not take issues or pull requests. Fork it and make it yours.