Akupara is a local-first web service with a browser UI, user, session and API-key authentication, a signed and hash-verified plugin library, internal and external plugin interactions, audio feedback, and update and integrity checks.
Developer documentation: README-DEV.md.
-
Local-first service with a browser UI — the service binds to
127.0.0.1on the port inresources/configuration.jsonand serves a single-page UI whose navigation switches between the home, settings and plugins pages. It exists so the service is reachable only from the device that runs it. Start it (see Run) and openhttp://127.0.0.1:<port>/in a browser; every page except the login page and the static assets requires a session, and a protected/api/*endpoint without a session returns401so the frontend redirects to/login. -
Accounts, sessions and roles — login credentials live in the
USERS.envvariable as Argon2id hashes (fixed saltakupara-salt,m=65536, t=2, p=1); a successful login issues a slidingakupara-refreshcookie (renewed on every authenticated response; 15 minutes of validity) whose session is held server-side per user, so several users can be logged in at the same time. It exists to keep the service restricted to known users. Log in at/login, and manage the accounts from the Users card: register a user, rename it, switch it between Standard and Admin, or delete it; a user marked"root": truein.envcannot lose admin status or be deleted, and only the root account may rename itself. A standard user may view the settings page but not administer it. -
API keys — with
API_KEYS_ENABLEDon, the API Keys card generates keys and the service accepts them in theX-Api-Keyheader on the management and external-interaction endpoints (api_key_or_admin_authenticated). Keys exist so plugins and scripts can act without a browser session. The.envstores each key Fernet-encrypted underAPI_KEYS, withAPI_KEY_ENCRYPTION_KEYgenerated on first run; the plaintext key is shown once in the card so it can be copied. -
Signed, hash-verified plugin library —
resources/plugins-lib/holds the catalogue of installable plugins as hash-range JSON files (<low>-<high>.json) plus areverse-index.jsonword index, andhashis the library's signed content hash. It exists to certify which plugin versions Akupara may run. A plugin is{"hash","name","description","repo","version","akupara_version","trust_mark"}, and at startup the service verifies the stored hash's PGP signature and recomputes the folder hash, so a modified or uncertified library stops the service; search it withPOST /api/plugins/searchor the Plugins page. -
Plugin discovery and upgrades — installed plugins live in
plugins/<hash>folders; the service verifies each folder's trust mark and content hash, compares the installed version with the plugin's GitHub releases, and offers or applies upgrades. It exists to keep installed plugins valid and current. Use the Plugins page (Check for Updates,Update All, per-rowUpdateandDelete) or the/api/installed-plugins*endpoints;dev-*folders skip every check and load only in development mode. -
Internal interactions (shared variables) — a
SHARED_MEMORYentry is{"name","type","value","editor","reader"}, whereeditorandreaderare plugin-name lists; a plugin present in both is kept ineditor. It exists so plugins can share values without a direct link. Register and edit variables from the Internal Interactions card (at least one plugin is required; updates are partial, so omitted fields are left untouched); each plugin's access is enforced per variable, and only an API key or an admin session may create or delete a variable. -
Plugin event subscriptions —
PLUGIN_EVENT_SUBSCRIPTIONSmaps an event name ([A-Za-z0-9_.-]+) to the sorted list of subscribed plugin names. It exists so plugins can address one another by event. Manage the subscriptions from the Internal Interactions card or through the/api/plugin-events*endpoints while internal interactions are enabled. -
External interactions (IP firewall and network worker) — with external interactions enabled,
ExternalInteractionsWorkerserves the marked endpoints on the device's first non-loopback IPv4 address on the same port, in parallel with the loopback server; the incoming IP list (allow,unknown,block) andEXTERNAL_INTERACTIONS_ALLOW_NEWdecide which remote clients pass, and a parallel outgoing list andEXTERNAL_INTERACTIONS_ALLOW_NEW_OUTGOINGexist for outgoing requests. It exists to expose the service to selected devices without opening it to everyone. Manage both lists from the External Interactions card; new incoming IPs are recorded automatically asunknownscoped toakupara. -
Audio feedback — the service plays a configurable sound for six events (
startup,acknowledge,warn,process,success,error); each event's file is set by its.envvariable or from the Customisation card, and playback requiresPLAY_AUDIOS. It exists to confirm actions audibly. Toggle playback, the startup sound and the log sounds from the Customisation card. -
Update checks — the service compares the signed project hash with the hash asset of the latest GitHub release, and the plugin-library hash with the hash at the latest commit, at startup and on demand. It exists to keep the deployment at a certified version. Check manually from the General and Plugins cards (
Check for Updates) or through/api/check-for-updatesand/api/check-for-plugin-updates;AUTOMATIC_UPDATE,AUTOMATIC_PLUGIN_LIBRARY_UPDATEandAUTOMATIC_PLUGIN_UPGRADEapply the result automatically. SetGITHUB_TOKENto raise the GitHub API rate limit. -
Integrity verification — the repo-root
hashandresources/plugins-lib/hashfiles each carry a hash plus an armoured PGP signature, and the service verifies the signature and recomputes the hash from the tracked files at every start. It exists to detect an illicit modification or a partial installation. The plugin library must always match its hash; the project may differ from its hash in development mode only, where it logs a warning instead of stopping. The General and Plugins cards show the effective and indicated version hashes side by side. -
Server-rendered settings by role — the settings page is rendered from card fragments under
ui/cards/settings/and sent with the session's role, so standard users see the General, Account and Customisation cards while admin-only cards are loaded only for administrators. It exists to avoid exposing administrative content to standard users. Open/ui/pages/settings.htmlfrom the UI.
- Install Python 3.10 or newer and make it available on
PATH. - Run
scripts\setup.baton Windows, orbash scripts/setup.shon Unix-like systems. The script creates a.venvvirtual environment and installsrequirements.txtinto it. To do it manually, create a virtual environment and runpip install -r requirements.txt. - Install GnuPG (
gpg) and make it available onPATH(C:\Program Files\GnuPG\binon Windows). It is required: the shippedhashfiles and plugin trust marks are verified withgpg --verifyagainstresources/plugins-lib/lorenbll-akupara-pub, and the service refuses to start when the signature cannot be verified. - Copy
.env.exampleto.envand setUSERSto a JSON list of{"username","password","admin","root"}, where eachpasswordis the Argon2id hash of the real password (scripts/generate-password-hash.jsprints a hash for a chosen password; the salt and parameters are fixed). - Review
resources/configuration.jsonto set the port, whether the UI routes are enabled (guiEnabled) and whether the service runs in development mode (development). - Keep the project structure intact so the service can find
resources/,src/andui/, and leave the signedhashfiles unmodified.
Start the service:
- Windows:
scripts\run.bat. - Unix-like:
bash scripts/run.sh.
Both scripts check for Python 3.10+, create or reuse .venv, install requirements.txt, and start src/main.py. Without --debug the service starts in the background with no console output; with --debug it runs in the foreground and writes a short trace of its progress.
To start it directly, run python src/main.py from the project root.
Add --debug to write DEBUG events to the log; without it, DEBUG events are not written. The log itself is described in README-DEV.md.
Configuration is split between two files: resources/configuration.json for the service itself and .env at the project root for runtime behaviour. Copy .env.example to .env; the service reads .env on every start and mirrors every change made through the UI back into it.
resources/configuration.json:
| Key | Default | Effect |
|---|---|---|
port |
49150 |
Port the service binds to on 127.0.0.1. Must be free and, on Unix-like systems, at least 1024 unless the process has elevated privileges. |
guiEnabled |
true |
When true, the UI routes (/, /login, /ui/*) are registered. Set to false for headless operation. |
development |
false |
When true, the project may differ from its recorded hash (a warning is logged and the update checks are skipped) and the manual and automatic project updates are refused. The plugin library is still verified strictly. |
.env:
| Key | Default | Effect |
|---|---|---|
USERS |
[] |
JSON list of {"username","password","admin","root","id"}, where password is the Argon2id hash of the real password and admin (default false) grants the administrator role. The service refuses logins while the list is empty. |
INTERNAL_INTERACTIONS |
false |
Enables internal interactions: shared variables and plugin event subscriptions, and their cards. |
SHARED_MEMORY_ENABLED |
false |
Switches shared variables on within internal interactions. Both this and INTERNAL_INTERACTIONS must be on for the shared-variable endpoints to work. When the key is absent from .env, the reader falls back to true. |
SHARED_MEMORY |
[] |
JSON list of {"name","type","value","editor","reader"} shared variables. type is one of string, list, dictionary, integer, float, boolean. |
PLUGIN_EVENT_SUBSCRIPTIONS |
{} |
JSON object mapping an event name to its sorted list of subscribed plugin names. |
API_KEYS_ENABLED |
false |
Enables API keys: the API Keys card, /api/api-keys*, and authentication through the X-Api-Key header. |
API_KEYS |
[] |
JSON list of {"name","key","id"}, where key is the Fernet-encrypted API key. Written by the service; the plaintext key is never stored, logged or returned after creation. |
API_KEY_ENCRYPTION_KEY |
generated on first run | Fernet key that encrypts and decrypts API_KEYS. Losing it makes the stored keys undecryptable. |
DISPLAY_PROMOTION |
true |
Shows the promotion line at the bottom of the UI. |
PLAY_AUDIOS |
true |
Enables audio playback for every event. When it is off, setting a sound file, toggling the startup or log sounds, or playing an event returns 403. |
PLAY_LOG_SOUNDS |
false |
Plays a sound for each logged event while PLAY_AUDIOS is on. |
PLAY_STARTUP_SOUND |
true |
Plays the startup sound once after the loading operations, while PLAY_AUDIOS is on. |
STARTUP_SOUND |
logo-reveal.wav |
Audio file from resources/audios/ played for the startup event; empty plays nothing. |
ACKNOWLEDGE_SOUND |
acknowledge.wav |
Audio file played for the acknowledge event; empty plays nothing. |
WARN_SOUND |
warn.wav |
Audio file played for the warn event; empty plays nothing. |
PROCESS_SOUND |
process.wav |
Audio file played for the process event; empty plays nothing. |
SUCCESS_SOUND |
success.wav |
Audio file played for the success event; empty plays nothing. |
ERROR_SOUND |
error.wav |
Audio file played for the error event; empty plays nothing. |
FFMPEG_PATH |
empty | Path to an ffmpeg binary; its neighbouring ffplay is used for playback when present. Without it, playback falls back to PowerShell's SoundPlayer on Windows or aplay elsewhere. |
EXTERNAL_INTERACTIONS |
false |
Enables external interactions and starts the network worker. When off, the firewall endpoints return 403. |
EXTERNAL_INTERACTIONS_INCOMING_IPS |
[] |
JSON list of {"ip","plugins","action","Note"} incoming firewall entries, where action is allow, unknown or block and plugins is a plugin-name list. |
EXTERNAL_INTERACTIONS_OUTGOING_IPS |
[] |
Outgoing firewall entries, in the same shape. |
EXTERNAL_INTERACTIONS_ALLOW_NEW |
false |
Lets through remote clients that are in the incoming list as unknown or absent from it. Requests from allow clients always pass and from block clients are always refused. |
EXTERNAL_INTERACTIONS_ALLOW_NEW_OUTGOING |
false |
Default policy for outgoing requests with no matching entry. |
AUTOMATIC_UPDATE |
false |
Applies an available project update (and restart) at startup. Refused in development mode. |
AUTOMATIC_PLUGIN_LIBRARY_UPDATE |
false |
Applies an available plugin-library update (and restart) at startup. |
AUTOMATIC_PLUGIN_UPGRADE |
false |
Upgrades installed plugins that have a newer release at startup. |
GITHUB_TOKEN |
empty | Optional token sent to GitHub hosts as Authorization: token <token>. It exists to raise the API rate limit (~60 to ~5000 requests per hour). Generate one with no scopes and no expiration, and add it to .env; without it, frequent checks may hit the limit, in which case version checks are skipped rather than failing, while operations that need GitHub data log an error and keep running. |
The sound-file defaults above are the values shipped in .env.example; when a sound key is absent from .env, the service falls back to the same file for STARTUP_SOUND, ACKNOWLEDGE_SOUND, SUCCESS_SOUND and ERROR_SOUND, and to an empty value for WARN_SOUND and PROCESS_SOUND.
- Open an issue on GitHub for bug reports, feature requests or help. Security issues should be reported privately as described in SECURITY.md.