Skip to content

Releases: tnandla/portfolio-os

v1.0.1 — security and performance hardening

Choose a tag to compare

@tnandla tnandla released this 06 Aug 08:44

A security and performance pass over the whole app. No schema change — upgrade by uploading the new code and rebuilt assets. Full detail in CHANGELOG.md.

Security

  • Decrypted credential secrets no longer persist in Livewire component state. Revealing a secret stored the plaintext in a public property, which Livewire serialises into wire:snapshot and echoes back to the browser on every later interaction with that page. Only the credential id is kept now.
  • Closed cross-project reads through client-controlled state. A partner could change userId on their own statement to read another partner's ledger; the approval queue rendered work items from unassigned projects; the links screen exposed budget totals for unauthorised projects.
  • Staff can no longer reassign tasks without tasks.assign, including through the bulk bar.
  • Exported CSVs neutralise spreadsheet formulas. A description starting =, +, - or @ executed on open in Excel and Sheets.
  • /_ops hardened: tokens shorter than 32 characters are refused with a 404, responses are no-store / no-referrer / noindex, cache-clear no longer rewrites layouts, and the asset recovery action discards downloads that are not plausibly the asset.
  • Livewire is served from the app, not a third-party CDN.
  • Attachment downloads are authorised and never renderable. Files are served against the permission of the record they hang off, as an attachment with an opaque content type, so an uploaded .html or .svg cannot execute on the app's origin.

Money integrity

  • Distribution shares add up exactly. Rounding each share half-up independently could pay out more than the profit (101 paisa split 50/50 paid 102).
  • Concurrent approvals can no longer double-credit the partner ledger, and an approved run keeps the ownership snapshot it was computed from.
  • Auto-expenses from article and link approval are idempotent under double-submit and concurrent requests, and a deliberately deleted recurring expense is not resurrected.

Fixed

  • Attachments can be downloaded at all. Task evidence, project files and expense receipts could be uploaded and deleted but never read back, so an approver could not open the evidence they were approving against.

Performance

The sixteen main screens went from roughly 3,100 queries to 170, displaying the same thing. Causes were per-request permission lookups, repeated settings reads against the database cache store, per-row aggregates called from Blade, a P&L report running three queries per project, and shared-expense allocations rebuilt (and rewritten) on read.

Lazy loading now throws outside production so the next N+1 fails in CI.

Honesty fix

Two-factor authentication is columns and a settings toggle only — no enrolment, no login challenge. The settings screen and SECURITY.md now say so, because the toggle could previously be mistaken for a control.

Docs

README rewritten around what the app does; setup moved to docs/INSTALL.md.


103 tests green on PHP 8.3, 8.4 and 8.5.

v1.0.0 — Portfolio OS

Choose a tag to compare

@tnandla tnandla released this 05 Aug 21:23

First complete release. Built as seven milestones, each shipped and verified before
the next began.

Milestone 1 — Foundation

  • Laravel application skeleton, authentication, password reset.
  • Many-to-many roles and permissions: 49 granular permissions across 5 seeded roles
    (admin, partner, supervisor, staff, accountant). Effective permissions are the union
    of a user's roles; no role column and no role switcher.
  • User administration, activation state, and an app settings store.
  • Application shell: navigation rail, top bar, command palette, mobile drawer and
    thumb bar.
  • Design system: tokens, dark mode, density modes and a Blade component library.
  • Seeders for roles, permissions, settings and task templates.

Milestone 2 — Projects & credentials

  • Project portfolio CRUD with status, CMS, niche, monetisation state, acquisition cost.
  • Per-project ownership shares validated to total 100%, and team assignment that scopes
    staff visibility.
  • Encrypted credential vault, with reveal gated by its own permission and every reveal
    written to an audit log.
  • Scheduled credential expiry alerts with configurable warning windows.
  • Portfolio dashboard.

Milestone 3 — Work

  • Tasks with checklists, priorities, due dates, attachments, comments and evidence
    on submit.
  • Recurring task generation and reusable task templates.
  • Article pipeline from brief to published, with word-count targets and per-article cost.
  • Link building log with per-project monthly budgets.
  • A single keyboard-driven approval queue across tasks, articles and links, optionally
    raising the matching expense on approval.

Milestone 4 — People

  • Attendance derived from the first login of the day, with a configurable late hour,
    plus supervisor leave and holiday marking.
  • Login history with IP and user agent.
  • Daily work logs.
  • Monthly scorecards aggregating output per person, costed against mixed pay rates
    (monthly salary and/or per-article, per-link, per-task).

Milestone 5 — Money

  • Revenue per project per month, entered directly or imported from CSV, with the FX
    rate frozen on each row so historical figures never re-convert.
  • Direct and shared expenses, receipt uploads, recurring expense templates, paid state.
  • Monthly profit and loss with shared costs allocated across projects in proportion
    to revenue.
  • Manual partner distributions computed from ownership shares; approving a run locks it
    permanently, and corrections are new adjusting entries.
  • Partner ledger and per-partner statements for capital, withdrawals and distribution
    credits.
  • All monetary values stored as integer minor units.

Milestone 6 — Deployment

  • FTP packaging scripts for shared hosting (deploy/package.sh, deploy/package.ps1)
    producing an app archive and a public archive.
  • Dual-path public/index.php that works both in the split shared-hosting layout and
    locally.
  • Token-gated /_ops/{action} maintenance route for hosts without SSH, disabled
    entirely when no token is set.
  • storage:link fallback that copies when symlink() is unavailable, plus a public
    media fallback route.
  • Pure-PHP database backup command requiring no mysqldump or shell access.
  • DEPLOYMENT.md.

Milestone 7 — AI assistant

  • Optional natural-language "ask your data" assistant and drafted monthly summaries,
    completely hidden when no provider key is configured.
  • Questions map onto a fixed whitelist of read-only report methods that re-apply the
    caller's permissions and project scope; a model never generates executed SQL.
  • Credentials, passwords, keys and bank details are stripped before any prompt is built.
  • Monthly spend cap, per-request token and cost logging, and response caching.