Strata is a free personal workspace for your dashboard, agenda, notes, whiteboards, subscriptions and files, built by Project Graphite.
-
Home: dashboards of widgets (agenda, tasks, pages, weather, news feeds, countdown, focus timer, app status and more), picked by time of day and device, and a wall display.
-
Tasks: grouped task lists and a board view, with status, descriptions, comments, attachments, repeating tasks, assignment and reminders. New tasks open in a quick dialog that reads dates, tags and priority, or in a full dialog or side panel, whichever you used last. Each epic remembers whether it shows as a list or a board. Ticking off a task can be undone, and it goes back to the status and column it had.
-
Agenda: Day, Week, Month and List views with drag to create, move and resize, events with guest RSVPs and date polls, followed outside calendars and a private calendar link. Events and calendars are yours; tasks due in your spaces show alongside them.
-
Pages: notes with live co-editing, offline drafts, version history, templates, mentions, comments and database views. Pages, Boards and Tasks show your own items; a space's items live under that space. Empty pages and boards you start show as drafts until they get a title or content. Pages and boards have short addresses, and old links still work. Pages and boards can be moved to the trash.
-
Boards: full-screen whiteboards drawn with Excalidraw, with templates, sticky notes and following a collaborator, embeddable in pages and events and editable by guests through a share link.
-
Bookmarks: saved links with a read-later list and a reader view.
-
Journal: a daily journal with a mood, and a one-click weekly review page.
-
Spending, one per account and never shared through a space: accounts and cards with balance history, budgets, installment plans, insights, net worth with assets and savings goals, statement import and CSV export, and recurring subscriptions with renewals, price history, reminders, home-currency totals at ECB rates and bank statement suggestions. The Android app captures bank texts and payment app notifications from allow-listed senders; confident ones are recorded, the rest wait for review.
-
Habits with check-ins and streaks.
-
Files: your own library of uploads, previews and downloads, with metadata stripped. Attachments in pages and tasks stay with their space and count toward the same storage quota.
-
Spaces: shared spaces that hold pages, tasks and boards, with members, invitations, tags, share links, activity and trash. Owners set defaults for new pages, boards, tasks and epics, choose who can invite people and make share links and how long links last, and can hand a space over, archive it or delete it. Everything else belongs to your account, with its own tags.
-
Trash and undo: anything moved to the trash can be undone from the notice that follows, and restored from the Trash for 30 days.
-
Confirmations: deleting for good, discarding edits and cutting off someone's access ask first. One-time secrets, such as tokens and recovery codes, stay on screen until you confirm you saved them.
-
Optional features, turned on in Settings: Goals, People, Recipes and meals and Tidy. Tidy suggests stale uploads in your spaces, and unread bookmarks, overdue goals and unplanned recipes of your own, to clear with bulk actions, undo and tagging rules.
-
Inbox: notices in the app, as push notifications and by email, chosen per kind and per channel in one place, with a daily journal reminder, RSVP answers and captured payments waiting for review.
-
Search and a command palette, an installable app, personal access tokens that can be limited to one space and can write page content, and webhooks.
-
Accounts:
- registration with email verification and resendable links;
- sign-in with rotating refresh sessions and reuse detection, rate-limited per session, so people sharing a network address don't lock each other out;
- password reset by email.
Registration never reveals whether an email address already has an account. The owner gets a notice instead. Failed sign-ins take the same time whether or not the account exists.
-
Two-step sign-in:
- authenticator-app codes (TOTP), set up from a QR code;
- ten recovery codes, each usable once and stored only as hashes;
- sign-in asks for a code after the password, with five attempts per sign-in;
- each code works only once.
-
Sensitive changes need your password and, with two-step sign-in on, a code. That covers the password, email, two-step settings and account deletion.
-
Devices and sessions:
- every signed-in device is listed, and any or all of them can be signed out;
- a signed-out device stops working straight away;
- a sign-in from a new device sends an email.
-
Security emails: new-device sign-ins, password changes and two-step changes.
-
Passwords: passwords found in data breaches are refused. The check sends only the first five characters of the password's SHA-1 hash to Have I Been Pwned.
-
Encryption: authenticator secrets are stored encrypted with AES-256-GCM, under a key that lives outside the database.
-
Settings: profile and time zone, email change, password, two-step sign-in, devices, data export and account deletion.
-
Security:
- a strict content security policy and isolation headers on every response;
- settings checked at start-up, so a missing secret stops the server instead of failing later;
- a request log with no query strings, bodies or email addresses;
- errors that never expose internals;
- scrypt password hashes that store their own cost settings.
-
Layout: the shared
@project-graphite/uishell, with a resizable sidebar on desktop that holds your apps in the order you set and a tree of spaces and pages, a tab bar on phones, and browser tabs named after the page. -
Errors say what went wrong and what to do next: inline on forms, with Try again on anything that failed to load, and in plain words when a followed calendar can't be read. A feature that breaks stays contained while the rest of Strata keeps working, and being offline, an update in progress and a server error each show one screen that retries by itself.
-
Privacy and terms pages.
- React 19, Vite, Tailwind CSS 4 and
@project-graphite/ui. - NestJS 12, Prisma 7 and PostgreSQL 18.
- Redis 8 for rate limits, widget caching and the health check; required in production.
- One container serves the built frontend and the
/api/v1API, and runs migrations when it starts.
docker compose up -d --build| Address | What it is |
|---|---|
| http://localhost:4104 | the app, through Vite |
| http://localhost:4102 | the server directly |
| http://localhost:4125 | Mailpit, which catches every email sent locally |
Vite does not see edits through a Windows bind mount, so restart the container after editing:
docker compose restart app frontendnode_modules lives in anonymous volumes filled from the image, so after a dependency change
rebuild and renew them:
docker compose up -d --build -V app frontendChecks run from app/. The last one keeps the frontend inside its bundle budget:
npm ci && npm run lint && npm test && npm run build
npm run check:bundle --workspace frontendGive an account the system manager role from inside the running container. Add --transfer to
hand the role over:
docker compose exec app npm run system-manager:grant --workspace server -- you@example.comIn production, open the app container's Terminal in the
Graphite Console, or docker exec into it on the VPS, and run:
npm run system-manager:grant --workspace server -- you@example.com| Folder | What it is |
|---|---|
app/server |
NestJS API, Prisma schema and hand-written migrations |
app/frontend |
React app and Capacitor Android project (app/frontend/android) |
app/Dockerfile |
development, build and production stages; the image is ghcr.io/project-graphite/strata/app |
Build the frontend and sync the Capacitor Android project from app/:
npm run android:build --workspace frontendThere are two flavours, built from app/frontend/android with ./gradlew assemblePlayRelease or
./gradlew assembleSideloadRelease. Both read notifications from allow-listed finance apps and
SMS senders; only sideload asks for READ_SMS to read the last 90 days of texts, and CI fails if
play does. Tagged releases (v*) attach the sideload build as strata.apk to the GitHub
release via .github/workflows/release.yml, linked from the Strata home page and phone setup
dialog.
Production runs at strata.project-graphite.com from
deploy/strata/values.yaml in platform. A push to main records the
image tag there, which deploys it. Uploaded files live in the files-data volume at /data/files
(FILES_ROOT, default /data/files). The backup service writes a pg_dump and a tar of the files
into the backups volume once a day and keeps BACKUP_KEEP_DAYS days of them.
These values live only in the organisation's secrets repository:
POSTGRES_PASSWORDREDIS_PASSWORDAUTH_ACCESS_TOKEN_SECRET, at least 32 charactersDATA_ENCRYPTION_KEY, 32 random bytes in base64 (openssl rand -base64 32). Never change it without re-encrypting the stored secrets, or every two-step sign-in breaks.EMAIL_HOSTandDEFAULT_FROM_EMAIL
The server refuses to start in production without REDIS_URL.
Optional: EMAIL_PORT (default 587), EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, VAPID_PUBLIC_KEY,
VAPID_PRIVATE_KEY, VAPID_SUBJECT and BACKUP_KEEP_DAYS (default 14), and:
TRUST_PROXY, the number of proxy hops whoseX-Forwarded-Foris trusted for client addresses (0to5, default1);SENTRY_DSN, which turns on error reports for unexpected 500s, scrubbed of cookies, tokens, bodies and query strings;METRICS_TOKEN, at least 32 characters, which serves Prometheus metrics at/api/v1/metricstoAuthorization: Bearer <token>;IP_HASH_SALT, at least 32 characters, the salt for the client address hashes stored with sign-in events (default: derived fromDATA_ENCRYPTION_KEY).
Pull request titles follow type(scope): summary using one of feat fix chore refactor docs test ci perf revert style build.