//depot/… — read + write

The whole depot,
in a browser tab.

p4-web is a lightweight web UI for Perforce — a P4V / Swarm alternative sized for individuals and small teams. Browse, blame, diff, search, review, see where the work is going, and even edit & submit, from one FastAPI process that shells out to the p4 CLI. No database server, no build step, no framework.

p4 diff2 //you/workflow#before //you/workflow#after
·@@ your daily Perforce workflow @@
1-install P4V on every machine you might read code from
2-stand up Swarm just to link a changelist in chat
1+./run.sh # one process, ~zero setup
2+open http://127.0.0.1:8080 # log in with your p4 account
3 protections still apply — every call runs as you
CL 69156
read-only core

Everything you open P4V for

Each card is backed by the real p4 command underneath — the app is a thin, honest wrapper, so results match your CLI exactly.

p4 dirs

Depot browser

Breadcrumbs, sortable listings, and a P4V-style lazy tree sidebar synced to deep links. Star a path and it lands on your home dashboard with its recent changes.

p4 print

File viewer

Syntax highlighting (vendored, offline), line-number gutter, revision dropdown, line permalinks.

p4 filelog

History & graph

Full history across renames and branches, plus an SVG revision graph with integration provenance.

p4 diff2

Diff tools

Unified and side-by-side everywhere: arbitrary revision pairs, folder diffs, shelved-vs-base.

p4 annotate

Time-lapse blame

Per-line changelist + author gutter, with a slider that re-blames the file at any point in history.

p4 changes

Changelists

Filter by user, path, text, or date — an incremental local index makes description and filename search fast, and the list keeps loading as you scroll.

p4 grep

Search

File-name search across depots and content search within a scope; matches jump to the highlighted line.

p4 labels …

Metadata browsers

Labels, jobs with fixes, branch mappings, streams, users & groups — all under one “More” menu.

app-side

Review

Comment on any line, including from inside a diff; @name someone and they get a badge; mark a changelist approved or needs-work. Plus rendered markdown, README panels, image previews.

app-side

Stats

Submits by day, week, month or year, by depot path with drill-down, and by account — three charts on one filter set, each bar a link into the matching changelists.

app-side

Switch it off

Reviews, comments, mentions, write operations, favorites, stats and the fast index can each be turned off — instance-wide by whoever runs the server, or just for yourself under Settings. A feature that is off stops answering, not merely showing.

CL 65181
write ops

My Changes: edit and submit from the browser

Each user gets a server-side workspace (p4web-<user>) with sparse sync — files appear only while you work on them.

What you can do

  • Create pending changelists, rewrite their description, or delete them
  • Edit text files in a web editor
  • Upload new or replacement files, binary-safe
  • Mark for delete, revert, shelve / unshelve
  • Submit behind a confirmation dialog showing the full file list

Guardrails

  • You can only touch your own p4-web pending changelists
  • Every write is recorded in data/audit.log
  • Login throttling per account and per IP; API rate limits per session
  • Perforce protections apply — the server never escalates
CL 65217
run.sh

Running in under a minute

One script creates the venv, installs three pinned dependencies, and serves.

$ git clone https://github.com/neoocean/p4-web.git && cd p4-web
$ export P4WEB_P4PORT=ssl:perforce.example.com:1666  # or inherit P4PORT
$ ./run.sh
# equivalent to:
$ python3 -m venv .venv
$ .venv/bin/pip install -r requirements.txt
$ .venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8080
# then open http://127.0.0.1:8080 and log in with a Perforce account

requires: Python 3.10+ · the p4 CLI on PATH · network access to your Perforce server

no git handy? download the source as a .zip

CL 65319
sec review

Security, the boring way

Reviewed end-to-end across two standing passes — code review, security audit, and a live red team against a running instance. The core boundaries:

  • Your ticket, your permissions. Every API call runs p4 with the session’s own user and ticket — Perforce protections apply exactly as in P4V.
  • No ticket leakage. P4TICKETS points at /dev/null for every subprocess; the server’s own tickets file can never authenticate a web user.
  • Passwords are never stored. Credentials are exchanged for a p4 login ticket; you can paste a ticket instead, Swarm-style.
  • Command allowlist. The p4 wrapper only permits known commands; write operations are confined to your own p4-web changelists and audited.
  • Hardened endpoints. Streamed raw downloads and bounded upload spooling (no 100 MB buffers), per-session rate limits, secure cookies, login throttling.
  • Boring deploys. The container runs as a non-root user, the bundled p4 client is verified against a published SHA-256, and every Python dependency is pinned to an exact version.
CL 65715
deploy

Deploy anywhere a shell runs

Sessions persist in SQLite (data/sessions.db), so restarts don’t log anyone out, and each one lasts exactly as long as its Perforce ticket. For ssl: servers the container entrypoint trusts the fingerprint for you — pin it with P4WEB_P4FINGERPRINT, or fall back to P4WEB_AUTO_TRUST=1 on a trusted network; outside Docker, run p4 trust once yourself.

Docker

The image bundles the p4 CLI; sessions and SSL trust live on the /data volume.

docker compose up -d

macOS · launchd

Always-on local service: edit the paths in deploy/com.p4web.launchd.plist, copy it to ~/Library/LaunchAgents/ — pairs well with a tailnet.

launchctl load ~/Library/LaunchAgents/com.p4web.plist

Linux · systemd

A standard unit file; edit paths, enable, done.

systemctl enable --now p4web
CL 65220
app layout

Small enough to read

app/
  main.py      FastAPI routes: auth, browse, file, diff, annotate,
               changes, comments, workspace write ops
  p4.py        p4 CLI wrapper: -G marshal parsing, path escaping,
               command allowlist, error → HTTP mapping
  sessions.py  cookie sid → {user, ticket}, plus favorites,
               comment threads, review states and mentions —
               all SQLite-backed
  index.py     per-user changelist search index (respects protections)
  workspace.py per-user p4web-<user> workspaces backing the write ops
static/
  index.html   login screen + app shell
  app.js       hash-routed SPA — no framework, no build
  style.css    light + dark, responsive below 768px
  vendor/      highlight.js, marked, DOMPurify (offline)

The entire stack is one Python process and a directory of static files. If something breaks, the code that broke is short enough to read over coffee — and every feature maps to a p4 command you already know.