Skip to main content
Argus has two kinds of caller. There are no public API keys, no OAuth for third parties, and no user-issued tokens.

Browser session

Signing in with Discord

Sign-in is a browser redirect flow, not a JSON exchange.
1

Start

The browser navigates to GET /api/auth/discord. Argus creates a single-use state, valid for ten minutes, binds it to this browser with a short-lived cookie, and redirects to Discord asking for the identify scope.
2

Callback

Discord redirects to GET /api/auth/discord/callback. Argus checks the state against the cookie, exchanges the code, and creates or updates the Argus account from the Discord profile.
3

Session

Argus sets the session cookie and redirects to the application root. Any previous session in that browser is replaced.
Signing in creates an account. It never grants a staff role. The cookie is HttpOnly, so scripts cannot read it. Same-origin fetch sends it automatically. On every request Argus re-checks the session’s expiry, the account’s status and the current staff membership. A suspended or disabled account is refused with ACCOUNT_DISABLED, and suspending an account revokes its sessions.

Origin check on changes

Every cookie-authenticated request other than GET must carry an Origin header equal to the application’s own origin (APP_URL). Anything else is refused with 403 INVALID_ORIGIN. Browsers add this header themselves on same-origin requests. Argus sends no CORS headers. A page on another origin cannot call this API with a user’s session.

Signing out

POST /api/auth/logout revokes the current session and clears the cookie.

Linking a Roblox account

A signed-in user can prove ownership of a Roblox account through GET /api/auth/roblox, an authorisation-code flow with PKCE. The callback must arrive in a session belonging to the user who started it. A Roblox account can be linked to one Argus account, and there is no unlinking endpoint.

Service credentials

An administrator can issue a credential for an integration with POST /api/admin/services. The response contains the token once; Argus keeps only its hash.
  • Scopes are permission keys. A credential can do exactly what its scopes allow. admin.manage and staff.manage cannot be granted.
  • It expires. The lifetime is 1 to 90 days, 30 by default. Revocation takes effect on the next request.
  • It is not a user. Endpoints that need a signed-in person refuse it with 403 USER_REQUIRED. Those are: the current user, sign-out, Developer Access, Roblox linking, all administration including staff membership, everything in the civilian portal, every official PNC change, and all MDT self-service.
  • It is never mixed with a cookie. If an Authorization header is present and invalid, the request fails with 401; Argus does not fall back to the session.
  • No origin check. Bearer requests do not need an Origin header.
Actions taken with a credential are attributed to it in the audit log by serviceId. A credential cannot claim to be a person. Each operation in the reference states its access: Browser session only, or Browser session, or a service credential.

Developer Access

Developer Access is temporary, internal tooling for the people building Argus. It is not part of the product’s permission model and is expected to be removed.
POST /api/auth/developer with the developer password gives the current session every permission in the catalogue. DELETE /api/auth/developer turns it off.
  • It is off unless the server has ARGUS_DEVELOPER_PASSWORD set. Clearing that variable disables it for sessions that already had it.
  • It changes no account, role or staff record, and does not affect the user’s other sessions.
  • It does not create a staff profile. Rules that need a real one still apply: for example booking on to a unit, starting a shift, or hosting a session.
  • Attempts are limited to five per account per 15 minutes. A wrong password, a malformed body and a disabled feature all return the same 401 AUTHENTICATION_FAILED.
  • GET /api/me reports it as developerAccess: true, with the expanded permissions.
The password is a deployment secret. It is never returned by the API and does not belong in documentation, source control or client code.

Rate limits

Limits are counted per caller, per minute, in the database, so they hold across server instances. Exceeding one returns 429 RATE_LIMITED with a Retry-After header in seconds.