The project tracker we run our own builds on, wired so Claude can update it
Shipyard is Autosterea's internal project-management system: a full-stack app with database persistence, real auth, a client portal, and an API key path so Claude can read and write the same projects we do.
The problem
We run a lot of client builds at once, and the work lives in a hundred places: notes, feature lists, user stories, milestones, open questions, the thing the client still has to sign off on. A read-only React mockup tracked some of it, but nothing stuck. Refresh the page and your edits were gone. Clients had no clean way to see status without us emailing screenshots. And the work of turning a meeting into a tidy list of features and stories was all manual. We wanted one system that holds the truth for every project, lets a client log in and sign off, and lets Claude do the data entry by talking to the same API the app uses.
What we built
The modules of the system.
Team dashboard
The home base for the people doing the work. Lists every project with KPI cards across the top, then drops into a single project with six tabs: Features, Stories, Milestones, Questions, Notes, and a Phase 2 backlog. Admins create projects and team members; everyone else works inside them.
Feature board
Each project is a real hierarchy: Section to Subsection to Feature. Every feature has a type (UI or backend), a status that walks from Not Started through UI Approved, Backend Ready, Testing, and Live, a priority, an assignee, a due date, and notes. Sections and subsections collapse so a big project stays readable.
User stories and acceptance criteria
Stories are written Given / When / Then, grouped by category, with a checklist of acceptance criteria you tick off as they pass. Status moves Not Ready to Ready to Test to Testing to Passed or Failed, so the story tab doubles as the test plan.
Notes that become work
A note holds the discussion, a list of decisions made, and action items. Action items can be converted straight into a feature or a user story, with the link tracked, so a meeting note turns into tracked work instead of dying in a doc.
Client portal
Clients get their own login with read-only access to their project, plus the one thing they actually need to do: sign off on UI features. No clutter, no edit buttons, just status they can trust and a button that records their approval.
Shareable links
For a one-off share without creating an account, you generate a password-protected link with an optional expiry. The server hashes the password with bcrypt, tracks access count and last-accessed time, and serves a locked-down read-only view of the project.
API keys for Claude
Admins mint ship_live_ keys from the settings page. Hand one to Claude and it can create projects, build out the full Section/Subsection/Feature tree, write stories with criteria, add milestones, and update statuses, all through the same REST API the web app uses. Keys can expire, get revoked instantly, and record when they were last used.
How it fits together
Shipyard is a two-tier app. The frontend is React 18 on Vite, with React Query handling server state and React Router for navigation, styled in Tailwind on the Autosterea palette. It talks to an Express API over Axios. The backend is Node and Express with Prisma ORM on PostgreSQL 16, which runs in Docker via docker-compose. Prisma models the whole domain: Users with roles, Projects, the Section to Subsection to Feature tree, UserStory with AcceptanceCriteria, Milestones, Questions, Notes with ActionItems, a Phase 2 backlog, ClientAccess, ShareableLink, ApiKey, and ProjectMember for team roles. The key design move is one auth middleware that accepts two credentials. If the request carries a Bearer token it verifies it as a ship_live_ API key against the database; if not, it falls back to the JWT stored in an httpOnly cookie from a browser login. Either way the request ends up with the same user object and runs through the same role checks. That is why the web UI and Claude hit identical endpoints with identical permissions. Routes split cleanly by resource (auth, projects, sections, features, stories, milestones, questions, notes, phase2, users, share, client, api keys, project members), each with a controller, and a shared error handler at the end.
Under the hood
The decisions that mattered.
One auth layer, two front doors
The interesting decision was refusing to build a separate API for Claude. Instead, authenticateToken checks the Authorization header first: if it sees Bearer it treats the token as an API key and runs verifyApiKey, which looks the key up, confirms it is active and unexpired, stamps lastUsedAt, and resolves to the key creator's user record. If there is no Bearer header it reads the JWT from the cookie and loads that user. From there everything is identical, including requireAdmin and requireTeamMember role gates. So Claude is not a special integration with its own surface area to maintain and secure. It is just another authenticated caller with a specific user's permissions, hitting the exact endpoints the React app already exercises. Less code, one place to reason about access, and no second API to drift out of sync.
API keys you can actually live with
Keys are generated with crypto.randomBytes and prefixed ship_live_ so they are recognizable in logs and configs. The full key is returned exactly once at creation, with a loud reminder to copy it now. After that the list view only ever shows a masked preview (first 15 and last 4 characters), never the raw value. Revoking is a soft delete that flips isActive to false rather than dropping the row, so the audit trail survives, and verifyApiKey rejects inactive or expired keys on every call. Every successful use updates lastUsedAt, so an admin can spot a stale key at a glance. It is the small set of things a key system needs to be safe in real use, and nothing more.
A client portal that is genuinely read-only
Client access is its own model and its own login path, separate from team members. A client sees only their project and gets exactly one write action: signing off on a UI feature, gated by a canSignOff flag. This matters because the same data powers the team dashboard and the client view, but they are two different worlds. The team needs to edit everything; the client needs to trust what they see and approve what is ready. Keeping client auth, client routes, and a dedicated client controller separate from the team side means there is no path for a client to accidentally edit a feature, and no shared edit code that could leak permissions.
Turning meetings into tracked work
A Note is not just text. It carries a decisions array and a list of ActionItems, and an action item can be converted directly into a Feature or a UserStory, with convertedToFeatureId and convertedToStoryId recording where it went. That closes the usual gap where a meeting produces a doc full of to-dos that never make it onto the board. Combined with the Claude API path, the workflow gets short: paste raw meeting notes to Claude, have it create the note, pull out decisions, and spin the action items into features and stories on the right project, all through the same endpoints a person would use.
Release log
What we shipped.
Started as a single read-only React component that tracked features but lost every edit on refresh.
Rebuilt as a real app: Express and Prisma on PostgreSQL in Docker, JWT auth, and a seeded project loaded with 50+ features so the dashboard had real data to work against on day one.
Features, Stories, Milestones, Questions, Notes, and a Phase 2 backlog, with the collapsible Section to Subsection to Feature hierarchy and Given/When/Then stories with checkable acceptance criteria.
Read-only client logins with UI signoff, plus password-protected, expiring shareable links with access tracking.
Added the API key system and the dual-auth middleware so Claude can create and update projects through the same REST API as the web app.
Section/subsection CRUD, inline acceptance-criteria editing, category-grouped stories, notes with decisions and convert-to-feature action items, and project team roles (Owner, Admin, Developer, PM, Client, Viewer).
The outcome
Shipyard gives Autosterea one place where every client build actually lives, with edits that persist, a client view that requires no email screenshots, and an approval button that records signoff. The payoff of building it as one API with two ways in is that Claude does the tedious part, taking a meeting or a feature dump and turning it into a structured project with sections, features, stories, and milestones, through the exact endpoints the team uses, with a key that can be revoked in one click. It is the tool we build with, on the same idea we sell: we build it, then we run it.
Put your business on autopilot