* [AI] Require admin for GET /secret/:name in OpenID mode The GET handler only verified an authenticated session, while the sibling POST handler enforced an admin gate when the active auth method is openid. A non-admin BASIC user in an OpenID multi-user deployment could enumerate which admin-managed bank-sync secrets were configured by probing 204 vs 404 responses. Factor the auth-method + admin check into a shared helper used by both POST and GET, and restrict the GET :name parameter to the known SecretName enum so unrelated probing returns 404 up front. * [AI] Simplify secrets auth guard after review - Use existing getActiveLoginMethod() helper instead of duplicating the SELECT inline; drop the redundant try/catch. - Validate the secret name against the SecretName enum before doing the auth-method DB query so bogus probes fail fast. - Switch the enum membership check from hasOwnProperty.call to the more idiomatic in operator. - Tighten the function-header comment to a single WHY line. - Drop the dead else-branch from the test helper. * [AI] Move response building back into the secrets handlers Reshape the helper as canManageSecrets(userId) - a pure predicate over the user. Each handler now owns its own 403 response so request/response plumbing stays inside the route handlers. * [AI] Undo testSecretName -> validSecretName rename Reuse the original testSecretName / testSecretValue constants; only the value of testSecretName changes to a real SecretName so the GET handler's enum check accepts it. * [AI] Add release note for #7862 * [AI] Validate POST secret name and tighten GET auth ordering - POST /secret/ now rejects names not in the SecretName enum with 400. - GET /secret/:name runs the admin check before the enum check so non-admins in OpenID mode get a uniform 403 regardless of whether the requested name is valid. - Add tests for both: admin POST success in OpenID mode, and POST 400 for unknown secret names. --------- Co-authored-by: Claude <noreply@anthropic.com>
This is the main project to run Actual, a local-first personal finance tool. It comes with the latest version of Actual, and a server to persist changes and make data available across all devices.
Getting Started
Actual is a local-first personal finance tool. It is 100% free and open-source, written in NodeJS, it has a synchronization element so that all your changes can move between devices without any heavy lifting.
If you are interested in contributing, or want to know how development works, see our contributing document we would love to have you.
Want to say thanks? Click the ⭐ at the top of the page.
Using the CLI tool
Node.js v22 or higher is required for the @actual-app/sync-server npm package
Install globally with npm:
npm install --location=global @actual-app/sync-server
After installing, you can execute actual-server commands directly in your terminal.
Usage
actual-server [options]
Available options
| Command | Description |
|---|---|
-h or --help |
Print this list and exit. |
-v or --version |
Print this version and exit. |
--config |
Path to the config file. |
--reset-password |
Reset your password |
Examples
Run with default configuration
actual-server
Run with custom configuration
actual-server --config ./config.json
Reset your password
actual-server --reset-password
Documentation
We have a wide range of documentation on how to use Actual. This is all available in our Community Documentation, including topics on installing, Budgeting, Account Management, Tips & Tricks and some documentation for developers.
Feature Requests
Current feature requests can be seen here. Vote for your favorite requests by reacting 👍 to the top comment of the request.
To add new feature requests, open a new Issue of the "Feature Request" type.