Glossary¶
About this document
Audience: everyone — humans and agents.
Purpose: keep vocabulary consistent across documentation, code, commits, and conversations.
Conventions
- Terms are listed alphabetically within each section.
- Bold term at the start of each entry.
- Cross-references use the form (see "Other Term").
- The first time a term is used in another document, it should match this definition.
1. Core domain¶
Account. A container for monetary value within a family, of one specific type (cash, debit, credit, investment) and one immutable currency. Accounts have a stored balance updated synchronously with every financial transaction touching them. Distinct from a user's app account (see "User account").
Account type. The kind of an account, drawn from a fixed set: cash (non-negative balance only), debit (any balance), credit (can go negative down to a configured limit), investment (holds positions and a cash balance, restricted to investment-related transactions). crypto is a planned future type.
Asset class. A categorization of a symbol: stock, etf, bond, crypto, mutual_fund, commodity, other. Used for grouping in reports and for choosing display behavior.
Author. The user who created a given entity. For a transaction, the user who recorded it; for an account, the user who created the account; etc. Stored as created_by_user_id or author_user_id. Used for attribution and for the "editor deletes own only" rule (see "Role").
Average cost. The cost-basis method used for positions in this app. When a position is bought multiple times at different prices, the position records a single average cost per unit, recomputed on each buy. Sales do not change average cost; they only reduce quantity. The application does not implement lot-based or specific-lot cost-basis methods.
Balance. The current monetary value held in an account, in the account's currency, stored in minor units. For non-investment accounts, the cash balance. For investment accounts, the cash balance only — position value is reported separately (see "Net worth", "Portfolio value").
Base currency. A currency designated as the default unit for cross-account aggregation. Each family has a base currency (see "Family base currency") used for reports like net worth.
Buy. An investment transaction kind. Decreases the investment account's cash balance and increases a position's quantity. Recalculates the position's average cost.
Category. A user-defined classification for transactions, organized in a two-level hierarchy (parent + optional children). Each transaction has at most one category. Each family has its own categories, seeded with defaults that the user can edit.
Cleared. A transaction status meaning the transaction is finalized and reflected in account balances. Contrast with pending (see "Pending").
Cost basis. The recorded cost of a position, in cost_currency, used to compute growth. In this app, always the average cost per unit times the quantity (see "Average cost").
Credit limit. The maximum negative balance allowed on a credit account. Stored as a positive integer; the account's balance is permitted to go down to -credit_limit.
Cross-currency transfer. A transfer between two accounts that hold different currencies. Modeled as two linked transactions, each in its own account's currency, with an explicit FX rate stored on the transfer group. The user enters each side as it appears on the relevant statement (see "Transfer", "FX rate").
Crypto wallet. A planned account type for holding crypto assets. In MVP, crypto is tracked using investment accounts; a dedicated crypto wallet type is deferred (see "Account type").
Currency. A unit of monetary value, identified by a 3-character code (e.g., USD, EUR, UAH, BTC). Each currency has a defined number of decimal places. Money is always stored as integer minor units in a specific currency (see "Money", "Minor units").
Deposit cash. An investment transaction kind that moves cash into an investment account. Participates in transfer pairs with other-account-side withdrawals (see "Transfer pair").
Dividend. An investment transaction kind. Increases the investment account's cash balance, linked to a symbol. Does not change position quantity. Stock-dividend / DRIP scenarios are handled as separate user-recorded transactions, not auto-linked.
Editor. A family role permitting create / edit / delete on entries the user authored, plus read access to everything else in the family (see "Role").
Expense. A transaction kind for non-investment accounts representing money flowing out. Decreases the account balance.
Family. The primary unit of data ownership in this app. Every transaction, account, category, tag, and report belongs to exactly one family. Users can be members of one or more families. Each user has one undeletable private family auto-created at registration (see "Private family", "Family member", "Membership").
Family base currency. The currency in which a family's net-worth and cross-account reports are denominated. Set at family creation, editable by an owner.
Family member. A user who has a role in a given family. Stored as a row in family.family_member. Distinct from "user," which is the authentication identity (see "User").
Family role. See "Role."
Family timezone. The IANA timezone associated with a family, used for report period boundaries (what counts as "this month"). Distinct from a user's display timezone, which affects only how UTC timestamps are rendered.
Fee. An investment transaction kind representing a brokerage or trading fee. Decreases the investment account's cash balance. Not linked to a specific position.
FX rate. An exchange rate from one currency to another at a specific date. Stored as a NUMERIC ratio. May be entered manually or, in deferred phases, fetched from external providers. Required for cross-currency transfers and for converting reports into a family's base currency.
FX rate history. The persisted record of FX rates over time. Reports use the closest-prior known rate to a given date.
GDPR delete. Hard deletion of a user's data, distinct from soft-deletion. Implements the "right to be forgotten." MVP does not include this; it's deferred.
GDPR export. A downloadable bundle of a user's data, in JSON or CSV, re-importable into another instance of the application. MVP includes this. Doubles as a portable backup.
Income. A transaction kind for non-investment accounts representing money flowing in. Increases the account balance.
Invitation. A pending request for a user to join a family in a specific role. Created by an owner. Persisted until accepted or rejected. Surfaced to the invitee through any client (web, bot) by polling.
Investment account. An account type that holds positions in symbols and a cash balance. Restricted to investment-related transaction kinds: BUY, SELL, DIVIDEND, DEPOSIT_CASH, WITHDRAW_CASH, FEE, SPLIT (see "Position", "Symbol").
Investment transaction. A transaction recorded against an investment account. Lives in investment.investment_transaction (a separate table from ledger.transaction). Has its own kinds: BUY, SELL, DIVIDEND, DEPOSIT_CASH, WITHDRAW_CASH, FEE, SPLIT. May participate in a transfer pair with a regular transaction via transfer_group.
Loan info. A planned info-only entity representing a loan the user has made or received. Does not affect net worth. Has a counterparty name (free text), principal, optional interest rate, status, and dates. Settlement is a status change, not a calculated event.
Logical date. The calendar date a transaction is logically dated to (e.g., "April 25, 2026"), as opposed to the timestamp at which the user recorded it. Stored as a DATE with no time and no timezone. What the user sees on a statement.
Minor units. The smallest indivisible unit of a currency. For USD this is the cent (1 USD = 100 minor units). For UAH this is the kopiyka (1 UAH = 100 minor units). For JPY this is the yen (1 JPY = 1 minor unit). For BTC this is conceptually the satoshi, but capped at 6 decimal places in this app (1 BTC = 1,000,000 minor units in this app). All money is stored as integer counts of minor units alongside the currency code.
Money. A pair of (amount in minor units, currency code). The application never uses floating-point types for money. In code, represented as a value object with a BIGINT amount and a 3-character currency code. In API JSON, represented as { "minor": <integer>, "currency": <code> }.
Net worth. The total monetary value across all of a family's accounts, expressed in family base currency, computed by summing each account's value (using investment-position pricing where relevant) and converting via current FX rates. Net worth excludes loan info entries by design.
Note. Free-text user-supplied commentary on a transaction. Optional. Searchable in transaction filters.
Owner. The family role with full control: manage members, change roles, edit / delete any data, delete the family (unless private). Each family has exactly one owner at a time (see "Role").
Pending. A transaction status meaning the transaction has been recorded but is not yet considered settled. Pending transactions are included in the audit and listings but may be rendered differently in UI. Transitioned to cleared via a dedicated action.
Portfolio value. The total value of all positions in an investment account or across all investment accounts in a family, expressed in the family's base currency, computed using current prices and FX rates. Distinct from cash balance.
Position. A holding of a specific symbol within a specific investment account. One position row per (account, symbol) while open; soft-deleted when fully sold. Records quantity, average cost per unit, and cost currency (see "Symbol", "Average cost").
Price history. The persisted record of a symbol's price over time. Each entry is (symbol, as_of_date, price, currency, source). Manually entered in MVP; future adapters fetch automatically.
Private family. The family auto-created for each user at registration. Undeletable. Hideable in UI but always present. Distinguished by an is_private flag.
Role. A family member's permission level, drawn from owner (full control), editor (create / edit, delete-own-only), viewer (read-only). Per-family, not global (see "Owner", "Editor", "Viewer", "Editor deletes own").
Sell. An investment transaction kind. Increases the investment account's cash balance and decreases a position's quantity. Does not change average cost. A position whose quantity reaches zero is soft-deleted.
Soft delete. Marking an entity as deleted via a deleted_at timestamp without physically removing the row. Soft-deleted entities are excluded from normal listings and computations but remain queryable for audit and export. Contrast with hard delete (see "Hard delete", "GDPR delete").
Hard delete. Physical removal of a row from the database. Used for entities where audit retention is not needed (tags, notifications) and as part of GDPR delete.
Split (D). A stock split transaction kind. Recorded as a ratio (e.g., 4-for-1). Updates a position's quantity and average cost per unit proportionally. Does not affect cash. Deferred — columns exist in schema but the kind is not implemented in MVP.
Symbol. An identifier for a tradable asset (a stock, ETF, bond, crypto, etc.). Symbols with an external identifier (ISIN, CoinGecko ID, etc.) are deduplicated instance-wide and shared across all users. Symbols without an external identifier are private to the creating user. A symbol records ticker, asset class, natural currency, external ID, and display name.
Tag. A free-text family-scoped label that can be applied to transactions. Many-to-many with transactions. Flat (no hierarchy). Distinct from category (see "Category"): a transaction has one category and zero or more tags.
Ticker. A short string identifying a symbol (e.g., AAPL, BTC, USDT-TRC20). Not necessarily unique across symbols (USDT on different chains has different tickers in this app, by design).
Transaction. A single recorded financial event in an account. Two physical tables hold transactions in this app: ledger.transaction for regular activity (income, expense, transfer) and investment.investment_transaction for investment activity (buy, sell, dividend, deposit_cash, withdraw_cash, fee, split). The split aligns storage with bounded contexts and matches the app's UX of separate regular and investment surfaces. "Transaction" without qualification typically refers to the regular kind; "investment transaction" is used when the investment kind is meant. Transfer pairs may span both tables — see Transfer. (see "Investment transaction", "Transaction kind", "Logical date").
Transaction kind. The semantic category of a transaction. For regular transactions: income, expense, transfer. For investment transactions: buy, sell, dividend, deposit_cash, withdraw_cash, fee, split (D). Each kind determines which fields are required and how the transaction affects account state. Each kind also implies a target table (regular kinds → ledger.transaction; investment kinds → investment.investment_transaction).
Transaction status. See "Pending", "Cleared".
Transfer. A movement of money from one account to another. Modeled as two linked transactions sharing a transfer_group_id, each in its own account's currency. The two transactions are created and updated together as a single logical operation. When both accounts are non-investment, both linked transactions live in ledger.transaction. When the transfer crosses between a regular and an investment account, the regular side lives in ledger.transaction and the investment side (a deposit_cash or withdraw_cash kind) lives in investment.investment_transaction; the transfer_group row links them.
Transfer group. The link between the two transactions of a transfer. Stores the FX rate used (when the transfer is cross-currency) and acts as a handle for treating the pair as a unit.
Transfer pair. Informal name for the two linked transactions of a transfer.
Viewer. The family role granting read-only access to all family data. Cannot create, edit, or delete anything (see "Role").
Withdraw cash. An investment transaction kind that moves cash out of an investment account. Participates in transfer pairs with other-account-side deposits (see "Transfer pair", "Deposit cash").
2. Identity, auth, access¶
Access token. A short-lived JWT proving a user's identity to the API. Sent in the Authorization: Bearer header. Default lifetime ≤ 15 minutes. Stateless: validated by signature, not via DB lookup.
API key. A credential issued to an external service for authenticating to the API. Hashed in storage; only the plaintext is shown once at creation. Has a scope (reader or writer) and an optional rate-limit override. The key holder is fully trusted to act on behalf of any user on the instance (see "External service", "Trust model").
Authentication. Verifying who the caller is. The API supports username+password, Google OAuth, Telegram-link (via the bot), and (post-MVP) TOTP and WebAuthn.
Authorization. Verifying what the caller is allowed to do. Family-membership-based: the caller's role in the relevant family determines permitted actions (see "Role").
Editor deletes own. The rule that an editor (a family role) can delete only entries they themselves authored. Enforced by the use case checking entity.created_by_user_id === acting_user_id against the role (see "Editor", "Author").
External service. Any non-user client of the API, authenticated via an API key. The Telegram bot is the project's first official external service. Self-hosters can add their own (CLI, custom integrations, third-party webhooks). The trust model treats external-service requests as instance-trusted (see "Trust model").
JWT. JSON Web Token. The encoding format used for access tokens. Signed by the API server; not encrypted (callers should not put secrets in the payload).
Operator. A privileged role bound to an environment-variable-supplied token (OPERATOR_TOKEN). In the browser, that token is exchanged for a short-lived HttpOnly cookie session used by protected operator endpoints. Has access to instance-level metadata and management endpoints (/v1/operator/*) but no access to user data. The token is not stored in the database.
Operator token. The bootstrap credential for operator access. Set via env var and used to mint the browser session cookie for protected operator endpoints.
Operator session. A short-lived HttpOnly cookie session issued after validating OPERATOR_TOKEN. Used by protected operator endpoints in the browser-based admin panel.
Recovery code. One of 12 single-use codes issued at user signup, used to reset a forgotten password without email infrastructure. Stored hashed; only the plaintext is shown once at generation. Regeneration invalidates the previous set.
Refresh token. A long-lived opaque token used to obtain a new access token. Stored hashed in the database (identity.session). Rotates on use: each refresh issues a new refresh token and invalidates the previous one.
Session. A row in identity.session representing one active refresh token. Listing sessions lets a user see and revoke logged-in devices (see "Refresh token").
Trust model. The agreement about who is responsible for what. The relevant case in this app: an API key is a system credential, not a user credential — the key holder can act on behalf of any user on the instance, and the operator (you, for the hosted instance; the self-hoster, for theirs) is responsible for key security.
User. An authenticated identity in the application. Distinct from "family member," which is a user's role in a specific family. A user belongs to one or more families.
User account. Informal: a user's app-level identity (credentials, sessions, profile). Not to be confused with a financial account (see "Account"). Where ambiguity is possible, prefer "user" or "user profile" instead of "account."
3. Architecture & code¶
Adapter. An implementation of a port. Lives in the infrastructure/ layer of a context. Examples: PostgresTransactionRepository adapts the domain's TransactionRepository interface to a real Postgres database; LocalFileStorage adapts the FileStorage port to the local filesystem.
Application layer. The middle layer of a bounded context, containing use cases (application services), DTOs, public read APIs, and event handlers. Orchestrates domain logic and infrastructure but contains no business rules of its own (see "Use case", "Domain layer", "Infrastructure layer").
Application service. Synonymous with "use case" in this codebase. A class with a single execute(input) method that orchestrates a single user-facing operation.
Audit entry. A row in audit.audit_entry recording one change to a family-scoped entity: who, when, what, before-state, after-state.
Bounded context. A modular boundary in the codebase, with its own domain, application, and infrastructure layers, and its own database schema. The contexts in this app are: Identity, Family, Ledger, Investment, Currency, Import, Reporting, Audit, Notification.
Context. Synonym for bounded context.
DDD. Domain-Driven Design. The methodology underlying the rich-domain-model and bounded-context discipline of this codebase.
Domain event. A typed message emitted by a context when something noteworthy happens (e.g., ledger.transaction.created). Persisted to shared.events. Subscribed to by other contexts and by the audit log.
Domain exception. An error thrown by domain code when a business rule is violated (e.g., InsufficientCashBalance, CreditLimitExceeded). Mapped to HTTP 422 by the API layer.
Domain layer. The innermost layer of a bounded context: pure entities, value objects, repository interfaces, domain services, domain events, and domain exceptions. Contains no infrastructure or framework imports (see Hard rules)
Driven port. An interface defined by the application describing what it needs from the outside (e.g., TransactionRepository, EventPublisher, Clock). Implementations are driven adapters in infrastructure/.
Driving port. An interface defined by the application describing operations it offers (e.g., a use case). Driving adapters (HTTP routes, bot commands, CLI commands) call them.
Event. Short for domain event.
Event bus. The infrastructure that dispatches events from publishers to subscribers. In MVP, an in-process implementation; designed to be swappable with a real broker (RabbitMQ, Redis Streams).
Event handler. A function or class that subscribes to a specific event type and reacts to it. Lives in application/event-handlers/. Idempotent (event ID is the dedup key).
Event payload. The body of a domain event, distinct from the envelope (id, type, occurred_at, version). Each event type has its own payload schema, versioned for evolution.
Hexagonal architecture. The architectural style of this codebase. Domain at the center, ports defining the boundary, adapters implementing the ports. Synonyms include "Ports & Adapters." The companion of choice is similar enough to Onion / Clean Architecture that the differences don't matter for code structure; they matter for vocabulary.
Idempotency. The property that a repeated request with the same Idempotency-Key produces the same result without re-executing the underlying operation. Implemented per API Conventions.
Idempotency key. A client-supplied string identifying a logical operation. Used to dedupe retries.
Infrastructure layer. The outermost layer of a bounded context, containing concrete adapters: HTTP routes, repository implementations, event subscribers' wiring, external service clients, job consumers.
Modular monolith. The deployment shape of this app: one process serving all bounded contexts, but internally organized so contexts have strict boundaries. Combines monolith simplicity with DDD discipline and a clean path to splitting if needed.
Optimistic locking. A concurrency-control technique using a version column on each shared mutable entity. Updates check that the version matches an expected value; mismatch returns a 409 conflict. No locks are held across requests (see API Conventions §7).
Port. An interface defining a boundary of the application. Driving ports are the application's offered operations; driven ports are what the application needs from outside (see "Adapter", "Driven port", "Driving port").
Public read API. A small, deliberately stable interface in a context's application/ layer that other contexts may call for synchronous reads. Distinct from internal repositories, which are not cross-context-callable.
Repository. An interface (in domain) and implementation (in infrastructure) for persisting and retrieving aggregates. One repository per aggregate root.
Use case. See "Application service."
Value object. A domain object without identity, equal by value (e.g., Money, DateRange, CurrencyCode). Immutable.
Version (entity). An integer column on a shared mutable entity used for optimistic locking. Incremented on every successful update (see "Optimistic locking").
Version (event). An integer field on a domain event indicating the schema version of its payload, allowing safe evolution of event shapes over time.
4. Infrastructure & ops¶
ACME. The protocol Caddy uses to obtain TLS certificates automatically (typically from Let's Encrypt). Used in self-hosted and cloud profiles.
BullMQ. The Redis-backed job queue library used in MVP for background work (CSV imports, future async tasks).
Caddy. The reverse proxy and static file server in front of the API. Provides TLS termination, serves the web SPA at /, proxies API calls at /api/*.
Cloud (deployment profile). The deployment profile where the project operates the instance for users. Differs from self-hosted only in optionally swapping container services for managed ones (managed Postgres, managed Redis, S3 storage).
Compose. Docker Compose. The orchestration tool used for all deployment profiles. No Kubernetes is used.
Cursor (pagination). An opaque token in list endpoints that encodes the position in the result set. Forward-only. Stable across requests for the same query (see API Conventions §5).
Dead-letter. A storage location for jobs or events that have permanently failed retry. Subject to operator inspection.
Init container. A one-shot container that runs migrations before the API process starts. Ensures schema is up-to-date before any request is served.
Local (deployment profile). The deployment profile for running on a personal machine. Same compose file as self-hosted, but without TLS and with localhost-only access.
Migration. A SQL file that evolves the database schema. Forward-only, idempotent, named with a UTC timestamp prefix. Run by an init container.
Operator surface. The collection of operator-only endpoints under /v1/operator/*, plus the operator-related env vars. Designed for instance management without access to user data.
Postgres schema. A logical grouping of tables within the single Postgres database. Each bounded context owns one schema (e.g., ledger, family). Distinct from a SQL schema-as-DDL; this is the DB-level namespace.
Profile (deployment). See "Cloud", "Self-hosted", "Local".
Redis. The in-memory data store used for: job queues, idempotency caching, rate-limit counters, report caching. Required infrastructure.
Reconciliation. A tool that recomputes account balances from transactions and reports drift. Runs on demand by the operator. Does not silently self-heal.
Self-hosted (deployment profile). The deployment profile where a user runs the project on their own server. Same code as cloud, with different secrets and possibly different domain. The user is the operator.
SPA. Single-Page Application. The web client architecture: a static bundle served by Caddy, calling the API client-side.
5. Documentation conventions¶
D (scope marker). Designed-for / Deferred. A requirement not built in MVP but architecturally accommodated — interfaces, schemas, and extension points exist; implementations are stubbed or absent. Concretely, "D" means "easy to add later without rewriting."
M (scope marker). MVP. A requirement fully implemented and shipping in v1.
MVP. Minimum Viable Product. The first shipping version, defined precisely by the M-marked requirements in Requirements.
Open item. A decision deferred to a later phase, tracked in Requirements §4 with an O-NNN ID. Distinct from D requirements (which are about features) — open items are about decisions (which framework, which library, etc.).
Requirement ID. A stable identifier for a requirement, of the form R-XXX-NNN (functional) or N-XXX-NNN (non-functional). Referenced in commits, PRs, and agent prompts to tie work to spec.
X (scope marker). Out of scope. A capability explicitly excluded from the design. The architecture does not go out of its way to accommodate X items; admitting one requires a vision-level conversation.
6. Notational conventions¶
Field naming. All persisted column names and all API field names use snake_case. All TypeScript identifiers in code use the conventions in Agents §7.2.
Identifier shape. All entity IDs are UUIDv7. References by string in this glossary and elsewhere are illustrative.
Money in prose. When citing amounts in this documentation, the form 12.34 USD is used for readability. In code and API JSON, money is always { "minor": int, "currency": code } (see "Money").
Quantity in prose. Same: 1.5 BTC in prose; { "value": 1500000, "decimal_places": 6 } in JSON; BIGINT in DB.
Schema in prose. When citing tables, the form <schema>.<table> is used (e.g., ledger.transaction). When citing fields, <schema>.<table>.<column> (e.g., ledger.transaction.amount_minor).
7. Out of scope (terms not used in this project)¶
These terms appear in adjacent products but are not used in this project. If they appear in code, comments, or PRs, that's a sign the project is drifting toward something it isn't (see Vision §2).
Bill. A scheduled payment to a third party. Not a concept in this app.
Category budget alert. A notification when a budget threshold is crossed. Not in MVP; deferred to a post-MVP phase if budgets are built.
Customer. A buyer of services. The app has users, not customers. Even in cloud, the relationship is "user of an open-source project," not "customer."
Invoice. A formal document requesting payment. Out of scope.
Lot (cost-basis). A specific tax-accounting record of a particular acquisition. Not used; this app uses average cost only (see "Average cost").
Payee. A counterparty in a payment system. Not modeled. Loan info has a "counterparty_name" as free text; transactions don't have payees.
Receipt. Loosely used to mean a transaction's attached document. The application term is "attachment" (deferred / D).
Settlement. Used colloquially in the loan context (a loan reaching settled status), not in any payment-rail sense.
Split (expense splitting). Dividing a transaction across multiple users (Splitwise-style). Out of scope. Distinct from "stock split" (which is in scope).
Subscription. A recurring payment to a service. Not detected, not categorized specially. A user can model it manually as a recurring transaction (when recurring transactions are built, post-MVP).
Tax. Anything related to tax calculation, reporting, or jurisdiction-specific handling. Out of scope.
8. Where to go next¶
- Why these concepts exist: Vision
- What each requirement using these concepts means: Requirements
- How these concepts are arranged in the system: Architecture
- How they map to tables and columns: Data Model
- How they're exposed at the API: API Conventions
- Conventions for using these terms in code: Agents