You run a bunch of websites with partners. Right now that probably means spreadsheets, a shared password doc, and a monthly “who is owed what?” argument.

Portfolio OS puts the sites, the passwords, the work, the people, and the money in one place — and it runs on plain PHP + MySQL shared hosting (the cheap kind with FTP and cron).

Dashboard

Clone it. Seed it. Click around for five minutes. If it clicks, you already know if it fits your partnership.

Try it in two minutes

You need PHP 8.3+, Composer, and Node (only to build CSS/JS on your laptop — nothing Node runs on the server).

git clone https://github.com/tnandla/portfolio-os.git && cd portfolio-os
composer install && cp .env.example .env && php artisan key:generate
touch database/database.sqlite && php artisan migrate --seed
npm install && npm run build
php artisan serve

Open http://127.0.0.1:8000 and sign in:

Account Password What you’ll see
admin@example.com password Everything
partner@example.com password Money + ownership view
supervisor@example.com password Approvals + team work
staff@example.com password Assigned work only
accountant@example.com password Revenue, expenses, P&L

Switch accounts once. The app changes shape a lot depending on who is signed in — that’s the point.

Stuck? Full setup, MySQL, config and troubleshooting: docs/INSTALL.md.


Features

Projects & credentials

  • Portfolio of every site: status, monetisation, ownership, open work
  • Encrypted credential vault per project (hosting, CMS, registrar, ads, analytics…)
  • Secrets only decrypt when someone with permission asks — every reveal is logged
  • Expiry warnings before domains / certs / access lapse

Work

  • Tasks with checklists, assignees, and recurring templates
  • Article pipeline from brief → draft → published (with cost)
  • Link-building log with per-project budgets
  • One approval queue for tasks, articles, and links (j/k move, a approve, r reject)
  • Approving an article or link can raise the matching expense for you
  • ⌘K command palette to jump or create from anywhere

People

  • Login counts as check-in (no separate punch clock)
  • Late after a grace window you choose
  • Monthly scorecards: tasks, articles, links — priced at each person’s rates
  • Mixed pay is normal: salary and/or per article / link / task

Money

  • Revenue by month (manual or CSV), with FX frozen per row
  • Expenses and shared costs allocated across projects by revenue share
  • P&L that partners can actually read
  • Ownership per project, enforced to total 100%
  • Partner distributions: preview → approve → locked forever (corrections are new entries)
  • Every amount stored as integer minor units — no float rounding surprises
  • Soft-delete only on financial records

Permissions that match real partnerships

  • Roles are many-to-many (partner and supervisor is fine — one merged UI, no role switcher)
  • 49 permissions across five seeded roles
  • Project access enforced in queries, not “hidden in the view”

Optional AI (off by default)

  • “Ask your data” box and drafted monthly summaries when AI_API_KEY is set
  • Credentials, passwords, and bank details never go to the model
  • No key → no nav, no route, no outbound call. The rest of the app is complete without it.

Built for shared hosting on purpose

  • PHP + MySQL only — no Redis, no Node on the server, no websockets, no Docker
  • Deploy with two zip files over FTP + two cron lines
  • Sessions, cache, and queues on database or files
  • Jobs safe to run late, out of order, or twice

Screenshots

Projects
Portfolio — every site, status, monetisation and open work.
Project detail
Project detail — ownership split, team, credential vault.
Tasks
Tasks — checklists, recurring work, bulk assignment.
Approvals
Approvals — one queue for tasks, drafts and links.
Profit and loss
P&L — revenue, direct cost, allocated shared cost, net.
Distributions
Distributions — profit splits by ownership, locked on approval.
Revenue
Revenue — monthly entry or CSV import, FX frozen per row.
Scorecard
Scorecards — monthly output per person, priced.
Command palette
⌘K — jump or create from anywhere.
Dark mode
Dark mode — a full second token set, no flash.
Articles
Articles — brief to published, with cost.
Attendance
Attendance — derived from logins.

Mobile isn’t a cut-down app — same navigation:

Mobile dashboard

Why shared hosting shaped this

The whole app runs on PHP and MySQL: no Node runtime on the server, no Redis, no queue daemon, no websockets, no container. Deployment is two zip files over FTP and a cron entry.

That constraint made a few good habits stick:

  • Sessions, cache and queues run on the database or filesystem. Nothing assumes Redis.
  • Every queued job is safe to run late, out of order, or twice — a cron drip does all three.
  • No reliance on exec(), proc_open() or symlink(). Backups are a pure-PHP SQL export.
  • Assets are built locally and uploaded. Fonts are self-hosted WOFF2 — no CDN in the runtime path.

Deploy it to a normal VPS and none of this hurts you — you just have headroom you are not using.

Security posture

  • Vault secrets are encrypted at rest with APP_KEY. The key is the lock: back up your vault before rotating it.
  • Decrypted secrets are never stored in component state, so they are not re-sent to the browser on later interactions.
  • Money mutations and every credential reveal are written to an audit log.
  • Financial records are soft-deleted only. Approved distribution runs are immutable.
  • The optional AI assistant is excluded from credentials in code, not by convention, and an LLM never generates SQL that gets executed — questions map onto a fixed whitelist of read-only reports.
  • /_ops/{action} is the sharp edge. It runs migrations and cache commands over HTTP for hosts with no SSH. It 404s when OPS_TOKEN is empty or shorter than 32 characters, compares tokens in constant time, is rate-limited per IP, and should be switched off the moment a deploy finishes.
  • Two-factor authentication is scaffold only: the database columns and a settings toggle exist, but there is no enrolment and no login challenge. Turning the toggle on protects nothing.

Report vulnerabilities privately — see SECURITY.md.

Stack

PHP 8.3+ · Laravel 13 · Livewire 4 · Alpine.js · Tailwind CSS 4 · Blade · MySQL 8 (SQLite locally) · Vite 8 · Pest 4 · Pint.

No React, no Vue, no Inertia, no SPA.

Tests

php artisan test        # 110 tests, SQLite in-memory
vendor/bin/pint --test  # code style

CI runs the suite on PHP 8.3, 8.4 and 8.5. Money calculations and approval flows require tests — see CONTRIBUTING.md.

Deploying

./deploy/package.sh    # → deploy/dist/app.zip + public.zip

app.zip goes to laravel_app/ outside the web root, public.zip to public_html/. Migrations run through the token-gated ops route because there is no SSH. Two cron entries handle the scheduler and a queue:work --stop-when-empty drip.

Non-techie Hostinger checklist: docs/SHARED_HOSTING_FOR_BEGINNERS.md. Full technical walkthrough: DEPLOYMENT.md.

Status and known gaps

All seven planned milestones are done and the app is in production use. See CHANGELOG.md.

Honestly:

  • Pick MONEY_BASE_EXPONENT before entering data. Stored integers carry no scale, so changing it later reinterprets every amount. There is no migration for that.
  • Column names keep their original _paisa / _pkr_paisa suffixes. They mean "minor units of the base currency" whatever currency you configure; renaming them would break existing installs for no functional gain.
  • Multi-currency is one base plus one optional input currency, not arbitrary per-project currencies.
  • Reports are tables. No charting library.
  • Wide financial tables scroll sideways on phones rather than reflowing into cards.
  • Editing happens in modals and side forms, not inline.
  • 2FA is scaffold only (see Security above).

Docs

docs/INSTALL.md Local setup, configuration, troubleshooting
docs/SHARED_HOSTING_FOR_BEGINNERS.md Short Hostinger checklist for non-techies
docs/USER_GUIDE.md What each role can do, and the short path to common tasks
DEPLOYMENT.md Shared-hosting deploy, ops route, cron, backups
docs/DESIGN_AUDIT.md The design system and its accepted gaps
CONTRIBUTING.md Setup, non-negotiable constraints, PR process
SECURITY.md Reporting vulnerabilities
CHANGELOG.md Release history