Jonathan KirkwoodandClaude Opus 4.8 69f12b0519 0.2.26: security hardening from audit (P2/P3 fixes)
Address the security-auditor findings the user selected (items 2, 3, 4):

Default admin credentials (P2): remove the fixed `Ten31` default. First
boot now generates a strong random admin password (secrets.token_urlsafe),
records it 0600 at /data/.admin-password, and surfaces it once via a new
"Show Initial Admin Password" StartOS action (CLI `show-admin-password`).
The stored password is cleared when the admin is reset (CLI reset-password)
or self-changes it (change-password endpoint).

Login hardening (P2): add a per-IP in-memory sliding-window rate limiter
(10 failures / 5 min -> 429 + Retry-After) in ratelimit.py; run a dummy
argon2 verify when the user is unknown so timing can't enumerate usernames;
keep a single generic 401 for unknown-user and wrong-password.

Hardening (P3): server process now runs unprivileged -- Dockerfile adds
uid 10001 appuser; start.sh (still root) chowns the mounted /data then
drops via `setpriv` before exec'ing uvicorn. Spreadsheet imports are
size-capped via storage.read_capped (413 past MAX_UPLOAD_SIZE) in the
schedule, capital preview, and batch paths. batch_import no longer returns
raw exception text (generic per-file messages).

Verified in the packed amd64 container: PID1 uvicorn runs as uid 10001,
/data owned 10001 with 0600 secrets; generated admin password retrievable
via CLI and logs in (200); 11th bad login -> 429; admin reset clears the
stored password. Tests: test_auth_hardening.py (4). Full suite 21 passed;
frontend tsc + StartOS bundle clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 19:09:27 -05:00
2026-06-07 19:23:26 +00:00

Ten31Portal

Internal system of record for Ten31 entities, holdings, positions, and quarterly valuation sign-off.

Accounts and access

Two kinds of accounts:

  • Internal staff (approver, cfo, fund_admin, viewer) — the full back-office app (entities, holdings, valuations, import, audit). approver and cfo also get the admin screens below.
  • External accounts (investor, fund_administrator) — a separate, entity-scoped portal. An external account only sees the entities granted to it.
    • Investor — sees, per fund, their latest capital-account value and history, plus documents shared to the fund or addressed privately to them (e.g. their K-1).
    • Fund administrator — sees assigned entities and can upload documents for them (shared or addressed to a specific investor).

Admin screens (Users / Documents / Capital Accounts, visible to approver and cfo) let you create an account with a username and password, check off which entities it can view, upload documents, and enter each investor's capital-account figures.

Login accepts a username or an email. The first admin is created from the CLI:

ten31portal-cli create-user --name "You" --username admin --role cfo --password '...'
# --email is optional; external accounts are normally created from the Users screen.

Prerequisites

  • Python 3.11+
  • Node.js 20+

Backend

cd backend
python -m venv .venv
source .venv/bin/activate
pip install -e .
uvicorn ten31portal.main:app --reload --port 8000

Health check: GET http://localhost:8000/api/health

Frontend

cd frontend
npm install
npm run dev

Opens at http://localhost:5173. Proxies /api to the backend on port 8000.

Project structure

ten31portal/
  backend/
    ten31portal/       # FastAPI application
      main.py          # App object and health endpoint
      config.py        # Env-based configuration
    pyproject.toml
  frontend/
    src/
      App.tsx          # Main component
      main.tsx         # Entry point
    vite.config.ts
  deploy/              # StartOS packaging (Issue 17)
  SPEC.md              # v1 issue specs
S
Description
mirror of gitea.ten31.ai Ten31AI/Ten31-Portal
Readme MIT
575 KiB
Languages
TypeScript 49.6%
Python 48.2%
Makefile 0.9%
Shell 0.4%
JavaScript 0.4%
Other 0.5%