App mobile
The native iOS app of Freelance OS. A Capacitor WebView that loads the app rendered on m.freelance-os.fr. You run your day, your tasks, your calendar, your messages, your CRM, your stats and the copilot from your phone. It runs entirely on the public API, exactly like a fully native app will.
Overview
The mobile app is the operator cockpit, built for one hand and a phone screen. Seven surfaces: Today (your day at a glance), Tasks, Calendar, Inbox, CRM, Copilot and Stats. A bottom tab bar, a side drawer to switch workspace and open the full navigation. The shell handles iOS safe areas, light/dark theme and two languages.
Under the hood, all data traffic goes through the Freelance OS public API (@fos/api-client), not the domain packages imported directly. That is the point of this version: every server action and every server component calls the same versioned HTTP surface, api.freelance-os.fr/v1, that a third-party client would use. A future fully native app cannot import domain code, it speaks HTTP. By routing the WebView through the same path, the app dogfoods the API and proves it is native-ready.
Auth stays local: the middleware bounces anonymous users to /login, the layout resolves the owner context (Supabase session plus active workspace). The user JWT is propagated to the API, which re-checks workspace membership and rescopes everything. Role gating decides which tabs show: a setter or a closer never sees a tab they cannot open, nor a money figure they are not allowed to read.
What the module does
Capacitor WebView around the rendered app
The app is a server-rendered Next.js PWA (server components, server actions, the copilot streaming route, auth middleware). It cannot be exported to a static bundle shipped inside the app, so the native iOS shell loads the running app over HTTP. In dev, localhost:4003 or your Mac LAN IP; in production, the HTTPS domain m.freelance-os.fr. The iOS bundle is fr.freelanceos.app, and the shell lets the web content own the env(safe-area-inset) values.
Entirely on the public API
Every screen reads and writes through @fos/api-client. withApiAuth takes the current Supabase session, extracts the JWT and passes it to the API on each call; the workspace goes in the {workspaceId} path param. Tasks, contacts, deals, interactions, inbox replies, the agenda, the analytics summary, revenue, funnel and copilot turn-saving all travel over this surface. Nothing bypasses the API to hit the database directly on the data path.
- Tasks: create, edit, change status, delete
- CRM: contacts, deals, interaction log, mark a booking completed or no-show
- Inbox: reply on a LinkedIn thread (sent via Unipile when connected, otherwise recorded locally)
- Copilot: streaming reply plus turn-saving into the Kernel's shared store
- Stats: summary, revenue and funnel over a range picked in the URL
Seven surfaces, one tab bar
Today and Copilot are always there: every member gets a home and an assistant. The Tasks, Calendar and CRM tabs appear based on the permission that unlocks them (workbench.read, booking.read, contacts.read). Inbox and Stats live in the More screen and the drawer, gated by their own rights. The Today screen makes one server pass: the day agenda, focus tasks, a 7-day analytics snapshot.
Role gating, money redacted
Permission keys come from @fos/domains-identity/permissions, the canonical registry the database persists and the Kernel gates on too. Tabs a role cannot open are hidden, not just disabled, so a setter never taps into a surface that would bounce them back to Today. Monetary figures (invoiced cash, MRR) only surface for finance roles; a setter with funnel.read sees the funnel but never the revenue.
Local auth and workspace switching
No host-to-workspace resolution like the member app: the owner picks their active workspace in-app, cookie-pinned. Switching workspace re-validates membership server-side, so a forged id can never move the user into a workspace they do not belong to. Editing the display name is scoped to the caller's own auth id, never a client-supplied id.
Offline and iOS packaging
A service worker serves an offline page when a navigation fails with no network. The iOS shell is generated by the Capacitor CLI and signed via an App Store Connect API key. A demo account (appreview@freelance-os.fr) on a demo workspace serves Apple review. This version is submitted to App Store review: the PWA runs in production, the native shell awaits Apple validation.
How to use it
- 01
Open your day
You launch the app on Today: the day agenda, your focus tasks, and if your role allows it, a 7-day analytics snapshot.
- 02
Move your tasks
From the Tasks tab you create, prioritize, check off and delete. Each action goes through the API and revalidates Today and Tasks.
- 03
Work your CRM
You open a contact, log an interaction, move a deal to another stage, mark a booking completed or no-show.
- 04
Reply in the inbox
You open a LinkedIn thread and reply. The send goes via Unipile if the channel is connected, otherwise the turn is recorded locally.
- 05
Ask the copilot
The copilot streams a reply with live workspace context and saves the turn into the same store the Kernel uses: you pick up the conversation on desktop.
- 06
Switch workspace
From the drawer or the More screen you switch workspace; tabs and figures readjust to your permissions.