Password-protecting an HTML report
The usual version of this doesn't work, and it's worth being precise about why before reaching for the thing that does.
A password inside the HTML is decoration
Every "password protect your HTML page" snippet does the same thing:
<script>
const pw = prompt("Password?");
if (pw !== "hunter2") document.body.innerHTML = "";
</script>
The check runs in the browser you are trying to keep out. The password is in the source. The content is already in the source. Disable JavaScript, or press Ctrl-U, and the gate is gone.
The encrypted variants — where the page ships ciphertext and derives a key from the typed password — are better in that the content genuinely isn't readable without the passphrase. They are still one shared secret, handed to everyone, revocable only by re-encrypting and re-sending. Fine for a single file to a single recipient; not an access model.
What you usually actually want
Nine times in ten, "password-protect this" means "not everyone should be able to open this" — and identity answers that better than a secret does, because it survives forwarding and it tells you who saw what.
Comma sets visibility per report:
| Level | Who can open it |
|---|---|
private |
You and people you invite by name |
team |
Members of the report's team |
registered |
Any signed-in Comma user with the link |
public |
Anyone with the link, no account |
Independently, the link carries a permission — view, comment, edit,
or none — and individuals can hold a role (owner, admin, editor,
commenter, viewer) that wins over whatever the link grants.
All of the above is on the free tier. A private report shared with three named reviewers is not a lesser version of a password; it is the thing a password was a bad approximation of.
When you do want a password or an expiry
Two gates are Enterprise:
- Password-protected links — a password required in addition to the link, checked server-side, where the content is not sent until it passes. This is the real version of the snippet at the top of the page.
- Expiring links — links that stop working on a date you pick. Useful for a report handed to an auditor, a client, or a candidate.
Domain-gated viewing is Enterprise too: domain visibility checks the
viewer's verified email domain server-side, so everyone at your company
can read it with no per-viewer invites. Full model in
sharing & access control.
When the content is the secret
If the report shouldn't be readable by the server that stores it — not just by other users — use a zero-knowledge encrypted report. It is encrypted in your browser with a key derived from your passphrase, and the server keeps only ciphertext.
The tradeoff is deliberate and worth stating plainly: encrypted reports can't be link-shared, scheduled, or sent to team destinations. The server can't read them, so it can't serve them to anyone but you. See the security model.
Setting it from a script
Access is writable over the API under a dedicated sharing scope, so a
token that publishes reports can't quietly widen who can see them:
curl -fsS -X PUT "https://commareports.com/api/v1/reports/$REPORT_ID/share" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "visibility": "team", "public_permission": "none" }'
Agents do the same through the MCP tool set_report_sharing. Reports
created over the API start private unless the create call says
otherwise — see API tokens and the
API reference.
What about basic auth on my own server?
It is a genuine server-side check, so it beats the JavaScript prompt by a wide margin. It also leaves you with one shared credential to distribute and rotate by hand, no record of who opened what, and no way to remove one person's access without changing it for everyone.
That is the whole argument for identity over secrets, in one paragraph.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history. Password, expiry and domain gates are Enterprise.