---
title: "Your people: sign-in, roles and the audit trail · Builder's guide · Chappell AI"
description: "GAAPcore has no user accounts, no passwords and no sessions, and it never will. How your people sign in, whether with passwords, single sign-on or a second factor, is your console's decision."
url: https://chappellai.us/guide/08-your-people-sign-in-roles-and-the-audit-trail/
---

Builder's guide · Section 8

# Your people: sign-in, roles and the audit trail

GAAPcore has no user accounts, no passwords and no sessions, and it never will. How your people sign in, whether with passwords, single sign-on or a second factor, is your console's decision.

## Keep people out of the books' database

Store people, sessions, approvals and the audit trail in the console's **own** database, owned by its own PostgreSQL login. The GAAPcore API's login must not be able to connect to it, and the console's login must not be able to connect to the books. The reference console's setup script tests both and fails if either connection works.

## Roles are yours

A sensible start:

| Console role | Typical permissions |
| --- | --- |
| Viewer | Read everything; no writes. Good for an outside accountant or an investor. |
| Bookkeeper | Day-to-day documents: orders, receipts, bills, invoices, payments, journals, bank matching. |
| Administrator | Everything a bookkeeper does, plus approvals, the period close, issuing statements, reversals of sensitive documents, people management. |

Enforce permissions **on the console server** before the call is passed on. Hiding a button in the browser is not a control.

## Record which person asked

GAAPcore records the writer, which is your console. Your console records the person. After every write, store a row with the person, the route, GAAPcore's `X-Request-Id`, and the ids in the reply (the document, its journal entry, any reversal). Make the audit table refuse updates and deletes. A record screen then shows both halves:

“Posted by **Sam Ortiz** through writer `console` at 2026-11-08 11:50 EST.”

Where GAAPcore offers a field for a person's name (`prepared_by`, `closed_by`, an approver), your **server** fills it from the signed-in session, ignoring whatever the browser sent.

## Session hygiene

-   An HttpOnly, SameSite=Strict session cookie; the browser holds nothing else.
-   Store sessions server-side as a hash of the token, so they can be revoked. Deactivating a person or changing their password ends their sessions immediately.
-   Lock an account briefly after repeated failed sign-ins.
-   Require a custom header on writes (the reference console uses `X-Console-Request: 1`), which a cross-site form cannot send.
-   Do not relay writer administration (`/v1/system/*`) through the console at all.
