Sprint Backlog 07
Sprint Mission
- finish reference implementation.
Stories
Active
| Tags | Headline | Time | % | ||
|---|---|---|---|---|---|
| Total time | 2:04 | 100.0 | |||
| Stories | 2:04 | 100.0 | |||
| Active | 2:04 | 100.0 | |||
| code | Add session information to database | 1:36 | 77.4 | ||
| code | Add UI for roles and permission management | 0:28 | 22.6 |
| Tags | Headline | Time | % | ||
|---|---|---|---|---|---|
| Total time | 53:20 | 100.0 | |||
| Stories | 53:20 | 100.0 | |||
| Active | 53:20 | 100.0 | |||
| agile | Sprint and product backlog refinement | 1:07 | 2.1 | ||
| infra | OCR scan notebooks for this sprint | 0:53 | 1.7 | ||
| code | Consider compressing payload | 3:05 | 5.8 | ||
| code | Latest currencies should have different colour | 1:28 | 2.8 | ||
| code | Do not expose temporal machinery in domain | 1:25 | 2.7 | ||
| code | Add account management support to qt | 3:43 | 7.0 | ||
| code | Add password reset functionality to accounts | 1:49 | 3.4 | ||
| code | Add core infrastructure for roles and permissions | 4:52 | 9.1 | ||
| code | Add exit confirmation dialog | 0:03 | 0.1 | ||
| code | Improve error messages in shell | 0:23 | 0.7 | ||
| code | Add support for relative time formatting | 2:01 | 3.8 | ||
| code | Setup windows laptop with WSL | 5:10 | 9.7 | ||
| code | Add listen/notify to shell | 2:45 | 5.2 | ||
| code | Define type traits for message grouping | 0:44 | 1.4 | ||
| code | Rename accounts to iam |
1:30 | 2.8 | ||
| code | Create the observability component | 1:59 | 3.7 | ||
| code | Consider renaming utility to system |
0:25 | 0.8 | ||
| code | Use chrono timepoints for domain entities | 1:44 | 3.2 | ||
| code | Use prebuilt qt | 0:26 | 0.8 | ||
| code | Add flags for currencies | 4:45 | 8.9 | ||
| code | Compilation fixes to current main | 3:47 | 7.1 | ||
| code | Ignore warnings on windows and OSX | 0:23 | 0.7 | ||
| code | Add sign-up workflow | 0:15 | 0.5 | ||
| code | Implement RBAC enforcement | 6:34 | 12.3 | ||
| code | Add session information to database | 1:36 | 3.0 | ||
| code | Add UI for roles and permission management | 0:28 | 0.9 |
STARTED Sprint and product backlog refinement agile
Updates to sprint and product backlog.
STARTED OCR scan notebooks for this sprint infra
We need to scan all of our finance notebooks so we can use them with AI. Each sprint will have a story similar to this until we scan and process them all.
Add AI generated sprint summary infra
At the end of the sprint, generate the sprint summary using the prompt.
Sprint Demo infra
Time spent doing the demo. This is the first one so it will be very basic.
COMPLETED Consider compressing payload code
Sounds quite simple to extend the binary protocol with a compressed payload. We just need some information in the header about it. We should support both compressed and uncompressed payloads. Play with compression algos in boost.
COMPLETED Add lock account request code
>>>>>>> 37dd037 ([agile] Backlog refinement)
Rationale: implemented last sprint.
As per gemini's comments, we should have a request to lock an account.
Notes:
- account unlock should return boolean rather than throw.
Test.
TEST_CASE("handle_login_request_locked_account", tags) { auto lg(make_logger(test_suite)); database_helper h; h.truncate_table(database_table); accounts_message_handler handler(h.get_context()); boost::asio::io_context io_context; const auto account = generate_synthetic_account(); create_account_request create_req(to_create_account_request(account)); BOOST_LOG_SEV(lg, info) << "Create account request: " << create_req; const auto create_payload = create_req.serialize(); run_co_test(io_context, [&]() -> boost::asio::awaitable<void> { auto result = co_await handler.handle_message( message_type::create_account_request, create_payload, "127.0.0.1:12345"); REQUIRE(result.has_value()); }); // 2. Simulate locking the account (A dedicated lock request should exist, // but for this test, we'll assume the handler has a method/logic for it // or that the system supports a lock request message type). // Assuming a lock_account_request exists or the account is locked internally. unlock_account_request lock_req; // Re-use struct for simplicity, assuming a dedicated lock is handled internally lock_req.account_id = create_account_response::deserialize( handler.get_account_id_by_username(create_req.username)).value().account_id; // Hypothetical internal method // In a real system, you'd send a dedicated lock message here. // For now, we rely on a separate mechanism to put the account into a locked state. // **NOTE**: For a proper test, a dedicated LOCK_ACCOUNT_REQUEST is needed. // 3. Attempt login with valid credentials for the now-locked account login_request login_req; login_req.username = create_req.username; login_req.password = create_req.password; BOOST_LOG_SEV(lg, info) << "Attempting login for locked user: " << login_req.username; const auto login_payload = login_req.serialize(); run_co_test(io_context, [&]() -> boost::asio::awaitable<void> { auto result = co_await handler.handle_message( message_type::login_request, login_payload, "192.168.1.100:54321"); REQUIRE(result.has_value()); const auto response_result = login_response::deserialize(result.value()); REQUIRE(response_result.has_value()); const auto& response = response_result.value(); BOOST_LOG_SEV(lg, info) << "Response: " << response; CHECK(response.success == false); // Check for an explicit error message/code related to account lock. CHECK(response.error_message.find("locked") != std::string::npos); }); }
COMPLETED Add tests for messaging handler code
Rationale: implemented last sprint.
We need tests in comms which create a test request and response and a test handler.
COMPLETED Add version support to entities code
Rationale: implemented last sprint.
We need a "version" field which is incremented automatically by a trigger. It is used as follows:
- domain entities have a version field which is loaded from database.
- if we try to save at version
nbut current version is notn-1, it should fail to save. - version is incremented automatically on save.
- display version in UI prominently (e.g. next to entity key, iso code for currencies) so that we can see when we reload.
COMPLETED Add skill to create new project component 2 code
Rationale: implemented last sprint.
Should create folders, CMakeLists, sample test, add to main make file etc. The new project should compile.
Links:
COMPLETED Latest currencies should have different colour code
After reloading we should mark latest currencies with a different colour so that it is obvious what was added. Consider colouring rows by recency.
Notes:
- valid to is the same as valid from, which is incorrect.
COMPLETED Do not expose temporal machinery in domain code
As per discussions with Claude:
> ok that looks good. but in terms of valid to/valid from: these are really > repository concerns, I think we are leaking them outside of the repository. I > wonder if it makes more sense for the domain entities to have a field such as > "created_at" which maps to the repository "valid_from" and not expose > temporarilty. what are your thoughts. do not change anything, we will deal > with that later, just want to capture this requirement for now.
That's a valid architectural concern. You're right that valid_from/valid_to are temporal table implementation details that are leaking into the domain layer.
Current state (leaky abstraction):
- Domain entity has valid_from and valid_to strings
- These are really repository/database concerns for temporal versioning (SCD Type 2 pattern)
- The UI now displays these raw database fields
Cleaner approach:
- Domain entity has created_at (or modified_at) - represents when this version was created
- Repository maps valid_from → created_at when loading
- valid_to stays internal to repository (used for querying current vs historical records)
- Domain doesn't need to know about the bi-temporal mechanics
Benefits:
- Domain entities are simpler and more intuitive
- Repository encapsulates temporal implementation details
- Easier to change temporal strategy without affecting domain
- created_at is more meaningful to users than "valid from"
I'll note this for future work. When you're ready to address it, the changes would involve:
- Rename valid_from → created_at in domain::currency
- Remove valid_to from domain (keep only in entity/repository layer)
- Update mapper to translate between them
- Update UI labels accordingly
COMPLETED Add account management support to qt code
At present we cannot add, remove etc accounts from the UI.
Notes:
- add history support for accounts.
- on delete we are automatically reloading.
- if a user has never logged in we should be able to see it. In fact we should
bucket accounts by recency:
- never logged in
- logged in a long time ago
- logged in recently
- is logged in
COMPLETED Add password reset functionality to accounts code
At present it is not possible to reset a user's password.
COMPLETED Add core infrastructure for roles and permissions code
At present we are monitoring authorisation at two levels:
- is logged in
- is admin
We need to introduce basic RBAC where we have roles such as:
- Trading
- Sales
- Operations: restricted access to some trading screens.
- Support: read-only, admin screens.
To start off with we can map these to read/write on CRUD dialogs.
Notes:
- rename
flag_initializertoflag_seederto make it consistent. Done.
COMPLETED Add exit confirmation dialog code
Add a simple dialog to check if the user really wants to exit.
COMPLETED Improve error messages in shell code
At present adding an account that violates some database constraint is not very informative:
ores-shell> ✗ Server returned an error
Client logs:
2025-12-16 10:20:39.538351 [TRACE] [ores.comms.net.client] send_request_sync: task completed. 2025-12-16 10:20:39.538431 [DEBUG] [ores.comms.net.client] Completed synchronous send request successfully. 2025-12-16 10:20:39.538494 [ERROR] [ores.comms.net.client_session] Server returned error: Repository error: Executing INSERT failed: ERROR: duplicate key value violates unique constraint "accounts_username_unique_idx" DETAIL: Key (username)=(newuser37) already exists. 2025-12-16 10:20:39.538567 [DEBUG] [ores.shell.app.repl] Sending logout request before exit.
Similarly, if the server goes down we don't know. Next messages just say:
accounts> list ✗ Request failed
Server logs:
2025-12-16 10:22:34.224094 [DEBUG] [ores.comms.net.connection] Waiting to read the next frame (skip_version_check=false) cancel_slot.is_connected=false 2025-12-16 10:22:34.224231 [ERROR] [ores.comms.net.connection] Network error in read_frame: stream truncated [asio.ssl.stream:1] 2025-12-16 10:22:34.224276 [ERROR] [ores.comms.net.client] Failed to read response frame, error network_error (0x7) 2025-12-16 10:22:34.224335 [WARN] [ores.comms.net.client] Connection lost 2025-12-16 10:22:34.224364 [TRACE] [ores.comms.net.client] send_request_sync: task completed.
COMPLETED Add support for relative time formatting code
"Relative Time Formatting" is part of the ECMAScript Internationalization API Intl.RelativeTimeFormat.
C++20's <chrono> library has extensive date/time support:
#include <chrono> #include <iostream> std::string relative_time(std::chrono::system_clock::time_point past) { auto now = std::chrono::system_clock::now(); auto diff = now - past; if (diff < std::chrono::minutes(1)) return "just now"; else if (diff < std::chrono::hours(1)) return std::to_string(std::chrono::duration_cast<std::chrono::minutes>(diff).count()) + " minutes ago"; // ... continue for hours, days, etc. }
COMPLETED Exit confirmation code
Rationale: Implemented.
We should ask if a user wants to exit before we exit.
COMPLETED Users cannot update their passwords code
Rationale: implemented.
At present it is not possible to update passwords, or any other property of an account.
COMPLETED Setup windows laptop with WSL code
Install all the tooling needed to build OreStudio in WSL on the windows laptop. Fix all the issues with Emacs bootstrap after elpaca move.
COMPLETED Add listen/notify to shell code
At present we cannot listen from the shell. This makes testing harder. Notification should show up in the next prompt like PSQL does.
COMPLETED Define type traits for message grouping code
At present we do:
auto result = client_->process_request< messaging::subscribe_request, messaging::subscribe_response, messaging::message_type::subscribe_request>(std::move(req));
This grouping is known in advance because these messages are defined together.
We should be able to create a type in the protocol that defines the grouping as
traits and then supply the group as the template argument, e.g.
messaging::subscribe, which unpacks the tuple.
COMPLETED Rename accounts to iam code
Gemini summary:
This pull request executes a significant refactoring effort by renaming the 'accounts' module to 'IAM' (Identity and Access Management). This change clarifies the module's responsibilities, which extend beyond simple account management to encompass broader identity and access control functionalities. The renaming impacts various aspects of the project, ensuring consistency in naming conventions across the entire codebase, from file structures and build systems to C++ namespaces and user-facing documentation.
Highlights:
- Module Renaming: The ores.accounts module has been comprehensively renamed to ores.iam to more accurately reflect its purpose as an Identity and Access Management component.
- Codebase-Wide Refactoring: This change involved updating directory names, C++ namespaces (ores::accounts to ores::iam), include paths, and references across all relevant files, including source code, headers, build configurations (CMakeLists.txt), and documentation.
- Documentation and Diagram Updates: All internal and external documentation, including CLAUDE.md, ores.hpp, Org-mode files (.org), and PlantUML diagrams (.puml), have been updated to reflect the new ores.iam naming convention and its expanded description.
COMPLETED Create the observability component code
We need some kind of observability context so that we can look at related log entries. This is sort of related to logging by thread except with coroutines we are no longer interested in threads per se but on some kind of "work identifier" which is preserved across function calls. For this we can have an observability context which is passed around.
Gemini summary;
This pull request integrates a new ores.telemetry component, establishing a robust framework for observability within the project. By adhering to the OpenTelemetry standard, it facilitates distributed tracing and log correlation, crucial for understanding complex system behavior. The design prioritizes coroutine-safety through explicit context management, ensuring that telemetry data can be accurately captured and propagated across asynchronous call chains.
Highlights:
- New Telemetry Component: Introduced a new ores.telemetry component, fully aligned with the OpenTelemetry specification, to enable distributed tracing and log correlation across the system.
- Core Observability Domain Types: Defined essential domain types including trace_id, span_id, span_context, span, log_record, and resource, providing a structured foundation for telemetry data.
- Unique ID Generators: Implemented thread-safe trace_id_generator and span_id_generator classes that embed timestamps and machine IDs, ensuring uniqueness, time-sortability, and efficient generation of identifiers.
- Coroutine-Safe Context Propagation: Provided a telemetry_context class designed for explicit, immutable context passing, making it safe and effective for tracing within coroutine-based asynchronous operations.
- Comprehensive Testing and Modeling: Included extensive unit tests for ID generation, context propagation, and span lifecycle management, alongside PlantUML diagrams for clear visualization of the component's architecture.
Notes:
- we should also add the notion of parents and children so that we can create a graph with the relationships. We should create specific messages for this which are not directly related to logging. The server can just keep track of the observability contexts in a table. We should link it back to each client etc.
- the observability context should also extend to the server so that we can filter all logs for that ID.
- consider using mongo IDs for this or create our own IDs:
Option A: 64-bit IDs (8 bytes) - Best balance cpp struct LogId { uint32_t timestamp; // seconds since 2020 uint16_t machine; // 0-65535 machines uint16_t sequence; // per-second counter // Encode to 11 Base64 chars or 8-10 Base62 chars };
Merged stories:
Log with session id
When we have a lot of clients all connecting we can't really see who is doing what in the server. We need an additional "tag" in logging to represent the session.
COMPLETED History should have a revert button code
It should be possible to choose a given version and ask the system to revert the currency to that version. It just makes an update with a new version to look like that version. It should also be possible to open at version. It shows the edit dialog in "read only mode" with the entity at that version. The tool bar should indicate this. It should also have a revert in that dialog.
COMPLETED History should have a reload button code
It should be possible to reload history without having to reload currencies. In an ideal world we should be able to listen to currency specific notifications. This will be needed in the future for other types of events.
COMPLETED Consider renaming utility to system code
Try to avoid the "grab bag" component that is littered with all sorts of random stuff. System implies extensions to core libraries like std and boost.
Gemini:
This pull request undertakes a significant refactoring effort to enhance the modularity and clarity of the codebase by splitting the monolithic ores.utility module into more focused components. The primary goal is to separate platform-specific functionalities into a dedicated ores.platform module, thereby improving dependency management and making the architecture more maintainable and extensible. This change ensures that core utilities remain lean, while system-level interactions are encapsulated in their own distinct layer.
Highlights:
- New Module: ores.platform: Introduced a new C++ module, ores.platform, specifically designed to house platform-specific utilities and abstractions for OS and hardware interaction. This module includes functionalities for environment variables, filesystem operations, and network information.
- Refactoring of ores.utility: Key components such as environment variable handling, filesystem operations (file I/O, directory search), and network information (hostname, MAC address, process ID) have been extracted from ores.utility and moved into the new ores.platform module. The ores.utility module has been updated to reflect these removals and now focuses on more general-purpose utilities like logging, UUID generation, string manipulation, DateTime, Converter (Base32/Base64), and program options.
- Dependency Updates: Build configurations (CMakeLists.txt) and PlantUML diagrams across various projects (ores.risk, ores.telemetry, ores.testing) have been updated to correctly reference the new ores.platform module where platform-specific functionalities are now located, ensuring proper linking and architectural representation.
- Codebase Migration: All existing code references to ores.utility::environment, ores.utility::filesystem, and ores.utility::net have been updated to point to their new locations within ores.platform::environment, ores.platform::filesystem, and ores.platform::net respectively, ensuring a seamless transition to the new modular structure.
COMPLETED Use chrono timepoints for domain entities code
Gemini:
This pull request undertakes a significant refactoring effort to enhance the modularity and clarity of the codebase by splitting the monolithic ores.utility module into more focused components. The primary goal is to separate platform-specific functionalities into a dedicated ores.platform module, thereby improving dependency management and making the architecture more maintainable and extensible. This change ensures that core utilities remain lean, while system-level interactions are encapsulated in their own distinct layer.
Highlights:
- New Module: ores.platform: Introduced a new C++ module, ores.platform, specifically designed to house platform-specific utilities and abstractions for OS and hardware interaction. This module includes functionalities for environment variables, filesystem operations, and network information.
- Refactoring of ores.utility: Key components such as environment variable handling, filesystem operations (file I/O, directory search), and network information (hostname, MAC address, process ID) have been extracted from ores.utility and moved into the new ores.platform module. The ores.utility module has been updated to reflect these removals and now focuses on more general-purpose utilities like logging, UUID generation, string manipulation, DateTime, Converter (Base32/Base64), and program options.
- Dependency Updates: Build configurations (CMakeLists.txt) and PlantUML diagrams across various projects (ores.risk, ores.telemetry, ores.testing) have been updated to correctly reference the new ores.platform module where platform-specific functionalities are now located, ensuring proper linking and architectural representation.
- Codebase Migration: All existing code references to ores.utility::environment, ores.utility::filesystem, and ores.utility::net have been updated to point to their new locations within ores.platform::environment, ores.platform::filesystem, and ores.platform::net respectively, ensuring a seamless transition to the new modular structure.
- Previous analysis
Gemini made an interesting review comment for
projects/ores.accounts/include/ores.accounts/domain/account_role.hpp
/** * @brief Timestamp when this role assignment was created. */ std::string assigned_at;
For consistency with other domain event objects and for better type safety, consider using
std::chrono::system_clock::time_pointfor assigned_at instead ofstd::string. Storing timestamps as strings in domain objects can be error-prone and makes date/time arithmetic difficult. The conversion from the database string format can be handled in the mapper.Suggested change:
-std::string assigned_at; +std::chrono::system_clock::time_point assigned_at;
Claude agrees:
Using
std::chrono::system_clock::time_pointis architecturally better for several reasons:Advantages:
- Type safety - Compiler catches errors; can't accidentally assign arbitrary strings
- Time arithmetic - Easy to compute durations, compare timestamps
- Idiomatic C++ - Standard library type for time handling
- Self-documenting - The type itself conveys "this is a point in time"
Trade-offs:
- Requires conversion code in mappers (string ↔ time_point)
- Need to handle timezone consistently (UTC recommended)
- Serialization to JSON/protocol buffers needs explicit handling
My recommendation: I agree with the reviewer's suggestion. The conversion complexity is localized to mappers, and the benefits of type safety throughout the rest of the codebase outweigh the cost.
However, this should be done consistently across all domain types (accounts, roles, permissions, etc.), not just RBAC. Otherwise you'd have mixed patterns - some types using strings, some using time_point.
Good candidate for a separate PR that:
- Adds timestamp conversion utilities to the mapper infrastructure
- Updates all domain types to use time_point
- Updates all mappers consistently
This keeps the RBAC PR focused and makes the timestamp improvement a coherent, reviewable change.
COMPLETED Use prebuilt qt code
At present we are having problems creating an installable gui. This is because Qt is hard to build and package from vcpkg. We should use prebuilt binaries for all platforms.
Links:
COMPLETED Windows clang build failures code
Seems like we are failing with a SEGFAULT:
<Catch2TestRun name="ores.comms.tests.exe" rng-seed="1383371051" xml-format-version="3" catch2-version="3.11.0"> <TestCase name="test_client_server_connection" tags="[networking][#net_client_tests]" filename="D:/a/OreStudio/OreStudio/projects/ores.comms/tests/net_client_tests.cpp" line="97"> <Expression success="false" filename="D:/a/OreStudio/OreStudio/projects/ores.comms/tests/net_client_tests.cpp" line="161"> <Original> {Unknown expression after the reported line} </Original> <Expanded> {Unknown expression after the reported line} </Expanded> <FatalErrorCondition filename="D:/a/OreStudio/OreStudio/projects/ores.comms/tests/net_client_tests.cpp" line="161"> SIGSEGV - Segmentation violation signal </FatalErrorCondition> </Expression> <OverallResult success="false" skips="0" durationInSeconds="2.17718"/> </TestCase>
COMPLETED Add flags for currencies code
Add flags for the currencies. Need database support. Display them in the currencies dialog.
Gemini PR summary:
This pull request introduces a foundational ores.assets component, establishing a comprehensive system for managing dynamic assets within the application. It provides structured domain models for images, tags, and their relationships, backed by a bitemporal database design that supports versioning and historical tracking. The new component includes a full data access layer with mappers and repositories, complemented by synthetic data generators and extensive test coverage to ensure its stability and functionality.
Highlights:
- New Component: ores.assets: Introduced a new component, ores.assets, dedicated to dynamic asset management, particularly for images and their associations.
- Domain Models: Added core domain models including image (for SVG content), tag (for categorization), image_tag (for many-to-many relationships), and currency_image (to link currencies with their flags).
- Bitemporal Database Support: Implemented robust bitemporal database tables (images, tags, image_tags, currency_images) with valid_from and valid_to timestamps, triggers for versioning, optimistic locking, and soft deletes, ensuring historical data integrity.
- Repository Layer: Developed a complete repository layer with mappers and repository classes for each domain model, facilitating CRUD operations and data persistence.
- Synthetic Data Generators and Tests: Included generators for synthetic data for all new domain models and comprehensive test suites for the repository classes, ensuring the reliability and correctness of the data access layer.
- SQL Schema and Utilities: Provided detailed SQL scripts for creating and dropping the new asset-related tables, along with a utility function (load_flag) to easily load SVG flag images into the database.
Previous analysis
Actually it is better to have a generic mechanism to upload an SVG and maybe tag it with an identifier. Then we could have a way to associate a currency with one of the available SVGs. The currency dialog could have a tab where users can select the SVG from a list. We can then have a table with SVG for currency which remembers these. This provides a generic solution for all cases where we have some kind of asset and an associated image.
SQL:
-- Ensure you have the necessary extension for generating random UUIDs CREATE EXTENSION IF NOT EXISTS pgcrypto; -- 1. icons table: Stores the SVG data CREATE TABLE icons ( -- Primary key using UUID (GUID) icon_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Unique key used by the application (e.g., 'usd_flag', 'btc_symbol') key VARCHAR(100) UNIQUE NOT NULL, -- The entire SVG content stored as plain text svg_data TEXT NOT NULL, -- Optional timestamps created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Index the 'key' column for faster lookups CREATE INDEX idx_icons_key ON icons (key); --- -- 2. tags table: Stores the list of available categories CREATE TABLE tags ( -- Primary key remains SERIAL as tags are usually created centrally tag_id SERIAL PRIMARY KEY, -- The name of the tag (e.g., 'currency', 'flag', 'commodity') name VARCHAR(50) UNIQUE NOT NULL ); -- Index the 'name' column for quick lookups and joins CREATE INDEX idx_tags_name ON tags (name); --- -- 3. icon_tags table: Junction table for the many-to-many relationship CREATE TABLE icon_tags ( -- Foreign key to the icons table (now a UUID) icon_id UUID NOT NULL REFERENCES icons(icon_id) ON DELETE CASCADE, -- Foreign key to the tags table (remains INTEGER) tag_id INTEGER NOT NULL REFERENCES tags(tag_id) ON DELETE CASCADE, -- Ensures that each icon/tag combination can only exist once PRIMARY KEY (icon_id, tag_id) ); -- Optional: Create a separate index for lookups by tag CREATE INDEX idx_icon_tags_tag_id ON icon_tags (tag_id);
STARTED Compilation fixes to current main code
Main is broken at present. Fix compilation and tests.
Gemini:
This pull request significantly improves the robustness and testability of the project. It addresses critical issues that could lead to test timeouts by ensuring the reliable generation of unique synthetic data. Furthermore, it resolves platform-specific build failures on macOS and expands the test suite with dedicated unit tests for key generator components, contributing to a more stable and well-tested codebase.
Highlights:
- Test Stability: Resolved potential infinite loops in unique synthetic data generation functions (e.g., for currency images, images, tags, and currencies) by implementing a suffix-based uniqueness mechanism when the faker library produces duplicate values.
- Cross-Platform Compatibility: Fixed macOS build errors related to std::chrono::system_clock::time_point construction by explicitly using std::chrono::duration_cast when converting from nanoseconds.
- Enhanced Test Coverage: Introduced new unit tests for tag, image, and currency_image generators to ensure their correct functionality, including single, multiple, and unique data generation scenarios.
- Standardized Testing Configuration: Centralized and updated Catch2 test arguments in the main CMakeLists.txt and individual project test CMakeLists.txt files for consistent reporting, specifically changing the XML reporter syntax.
STARTED Ignore warnings on windows and OSX code
At present we have a number of spurious warnings on both windows and OSX. Try to see if we can get CDash to ignore them.
Notes:
- Raised a Kitware ticket.
Links:
STARTED Add sign-up workflow code
It should be possible to sign-up and create a new account.
Gemini:
This pull request introduces a comprehensive user self-registration feature, enabling new users to create accounts independently. It encompasses updates across the backend messaging system, a new service layer for robust validation and account creation, and a fully integrated user interface within the Qt client. The functionality is controlled by system-level feature flags, ensuring administrators can manage its availability and behavior.
Highlights:
- User Self-Registration (Signup) Workflow: Introduced a complete workflow for new user self-registration, allowing unauthenticated users to create accounts when enabled by a feature flag. This includes new messaging protocols, a dedicated service for handling signup logic, and a frontend UI in the Qt client.
- New Messaging Protocol and Handler: Added signup_request and signup_response messages to the accounts messaging protocol. The accounts_message_handler now includes a handle_signup_request method to process these messages, leveraging the new signup service.
- Dedicated Signup Service: A new signup_service has been implemented to encapsulate the business logic for user registration. This service performs crucial validations such as checking for enabled user signups, username and email uniqueness, and password policy compliance before creating new accounts.
- Qt Client Integration: The Qt client has been updated with a new SignUpDialog and a ClientManager::signup method. This provides a user-friendly interface for self-registration, including input validation and asynchronous communication with the backend.
- System Flags for Control: Enhanced system flags with user_signups to enable/disable the self-registration feature and signup_requires_authorization (though not yet implemented) to control whether new accounts need admin approval.
- Account Repository Enhancement: The account_repository now includes a read_latest_by_email method, essential for ensuring email address uniqueness during the signup process.
- Protocol Version Increment: The communication protocol minor version has been incremented to 13.1 to reflect the addition of new message types and error codes related to the signup workflow.
At present this fails:
2025-12-22 00:21:41.174342 [DEBUG] [ores.comms.net.server_session] Received message type signup_request (0x2030) 2025-12-22 00:21:41.174444 [DEBUG] [ores.comms.messaging.message_dispatcher] Dispatching message type signup_request (0x2030) 2025-12-22 00:21:41.174536 [WARN] [ores.comms.service.auth_session_service] Authorization failed for signup_request (0x2030) from 127.0.0.1:38118: not authenticated 2025-12-22 00:21:41.174576 [WARN] [ores.comms.messaging.message_dispatcher] Authorization denied for signup_request (0x2030) from 127.0.0.1:38118 2025-12-22 00:21:41.174620 [ERROR] [ores.comms.net.server_session] Message dispatch failed: 10 2025-12-22 00:21:41.175062 [DEBUG] [ores.comms.net.server_session] Sending error response: Authentication failed (correlation_id=1) 2025-12-22 00:21:41.175190 [DEBUG] [ores.comms.messaging.frame] Serialised frame error_response (0x4), size: 104 2025-12-22 00:21:41.175266 [DEBUG] [ores.comms.net.connection] Writing frame of size 104 type: error_response (0x4) sequence: 2 2025-12-22 00:21:41.175499 [DEBUG] [ores.comms.net.connection] Successfully wrote frame 2025-12-22 00:21:41.175569 [DEBUG] [ores.comms.net.server_session] Sent response for message type signup_request (0x2030) 2025-12-22 00:21:41.175636 [DEBUG] [ores.comms.net.connection] Waiting to read the next frame (skip_version_check=false) cancel_slot.is_connected=false 2025-12-22 00:21:41.180487 [ERROR] [ores.comms.net.connection] Network error in read_frame: stream truncated [asio.ssl.stream:1] 2025-12-22 00:21:41.180768 [INFO] [ores.comms.net.server_session] Client disconnected 2025-12-22 00:21:41.183120 [ERROR] [ores.comms.net.server_session] Exception in notification writer: co_await: Operation canceled [system:125] 2025-12-22 00:21:41.183430 [DEBUG] [ores.comms.net.server_session] Notification writer coroutine ended 2025-12-22 00:21:41.184552 [INFO] [ores.comms.net.server_session] Unregistering session '127.0.0.1:38118' from subscription manager 2025-12-22 00:21:41.184856 [INFO] [ores.comms.service.subscription_manager] Unregistered session '127.0.0.1:38118' (had 0 subscriptions, remaining sessions: 0) 2025-12-22 00:21:41.185205 [INFO] [ores.comms.net.server_session] Session ended for client: 127.0.0.1:38118 2025-12-22 00:21:41.185372 [DEBUG] [ores.comms.net.server] Session completed, active connections: 0
STARTED Implement RBAC enforcement code
Conversation with Claude:
RBAC permission checking is not yet implemented. The handlers currently use simple session->is_admin checks rather than checking permissions via the authorization_service.
For example in handle_lock_account_request:301:
// Current: simple admin check if (!session->is_admin) { ... } // Should be: permission check if (!auth_service_.has_permission(session->account_id, permissions::accounts_lock)) { ... }
The RBAC infrastructure exists (permissions, roles seeded in DB), but it's not being enforced. To properly test RBAC, we need to:
- Inject authorization_service into accounts_message_handler
- Replace is_admin checks with permission checks
- Store permissions in session (optional, for performance)
Would you like me to implement proper RBAC enforcement in the message handlers? This would involve:
- Adding authorization_service to the handler
- Checking specific permissions like accounts:lock, accounts:reset_password, etc.
- Non-admin users with the right role/permissions would then be allowed to perform those operations
> actually maybe we should split that into a separate PR as this one is already > very large. I'll merge it as is
● Good call. The current PR already includes:
- RBAC domain types (permission, role, account_role, role_permission)
- Database schema for RBAC tables
- authorization_service for managing permissions/roles
- rbac_seeder for seeding predefined permissions and roles
- Protocol messages for RBAC operations (list_roles, list_permissions, assign_role, etc.)
- Protocol version bump to 13.0
The follow-up PR would implement:
- Injecting authorization_service into message handlers
- Replacing is_admin checks with proper permission checks
- Testing non-admin users with specific role permissions
This keeps the current PR focused on RBAC infrastructure and the next one on enforcement.
Notes:
- add tests for all new entities. Code coverage has dropped a lot after adding them.
STARTED Add session information to database code
At present we register the last login etc. It would be nice to record every session as well, including the duration.
Notes:
- record bytes sent/received.
- keep track of geolocation so we know where users are logging in from.
- calculate statistical measures (average session duration, average bytes etc) and consider plotting them. Also in the aggregate so we can see patterns over time.
- keep track of the session id so we know what client we are using, the version etc. We should display this information in the accounts dialog.
- on the main accounts dialog we should show the number of open sessions.
STARTED Add UI for roles and permission management code
We added the core infrastructure but we need a way to manage it from Qt and from the shell.
Sign-up dialog should be disabled if feature is disabled code
At present you can click on sign up and fill in all details but then you get a server error:
2025-12-24 10:46:48.212905 [DEBUG] [ores.iam.messaging.accounts_message_handler] Request: signup_request{username=newuser4, email=newuser4@example.com}
2025-12-24 10:46:48.213049 [INFO] [ores.iam.service.signup_service] Signup attempt for username: newuser4, email: newuser4@example.com
2025-12-24 10:46:48.213144 [WARN] [ores.iam.service.signup_service] Signup rejected: signups are disabled
2025-12-24 10:46:48.213186 [WARN] [ores.iam.messaging.accounts_message_handler] Signup failed for username: newuser4, reason: User registration is currently disabled
2025-12-24 10:46:48.213898 [DEBUG] [ores.comms.messaging.compression] Compressed 68 bytes to 60 bytes using zlib (0x)
Makes more sense for sign-up button to also be disabled in client. Trouble is, we do not know as we are not connected. This means the handshake has to provide some kind of flag saying sign-up is enabled or disabled.
Add sign-up approval workflow code
Should also handle invite codes, etc.
Clean up test databases code
Sometimes we end up with a few test db's left:
template1=> \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges
-------------------------+----------+----------+-----------------+-------------+-------------+--------+-----------+-----------------------
oresdb_test_210496_4571 | ores | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
oresdb_test_214711_1947 | ores | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
oresdb_test_215309_7714 | ores | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
oresdb_test_216112_5205 | ores | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
oresdb_test_216966_9268 | ores | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
postgres | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
template0 | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | | =c/postgres +
| | | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | | =c/postgres +
We should have a script that tidies these up.
Tests are logging to stdout in github code
There is a lot of stdout output in CDash. We need to understand why that is.
Test output
[2025-12-16 20:31:44.600851] [0x00000001f1d1a200] [info] Test run starting, creating test database
[2025-12-16 20:31:44.601397] [0x00000001f1d1a200] [info] Generated test database name: oresdb_test_4816_1242
[2025-12-16 20:31:44.601401] [0x00000001f1d1a200] [info] Creating test database: oresdb_test_4816_1242
[2025-12-16 20:31:44.601434] [0x00000001f1d1a200] [info] Creating context. Configuration: {"database_options":{"user":"ores","host":"localhost","database":"postgres","port":5432},"pool_size":1,"num_attempts":10,"wait_time_in_seconds":1}
[2025-12-16 20:31:44.630284] [0x00000001f1d1a200] [info] Finished creating context.
[2025-12-16 20:31:44.771670] [0x00000001f1d1a200] [info] Successfully created test database: oresdb_test_4816_1242
[2025-12-16 20:31:44.771733] [0x00000001f1d1a200] [info] Setting ORES_TEST_DB_DATABASE environment variable to: oresdb_test_4816_1242
[2025-12-16 20:31:44.771738] [0x00000001f1d1a200] [info] Environment variable set successfully
[2025-12-16 20:31:44.771740] [0x00000001f1d1a200] [info] Test database ready: oresdb_test_4816_1242
[2025-12-16 20:31:44.772151] [0x00000001f1d1a200] [info] Test case starting: delete_account_response_failure
[2025-12-16 20:31:44.772158] [0x00000001f1d1a200] [info] Tags: messaging #messaging_protocol_tests
Add a dashboard for users code
Examples:
User interface options code
This story keeps track of configuration points in the UI which should be added at some point:
- display time in human readable form - e.g. last month, 3 months ago, etc.
- auto-reload. If a dialog receives new data, reload it automatically. Defaults to off.
Notes:
- we need a UI to manage the UI options.
- should be stored in the server and cached locally with a sqlite db.
New feature flags code
- allow sign-ups. If true, users can create their own accounts.
- auto-authorise sign-ups. If true, the account is automatically created. If false, admin user has to authorise it.
Notes:
- we need a UI to manage the UI options.
Assorted fixes to currency dialog code
At present the following is not working or is a snag:
- can we make the details windows unmaximisable.
- currencies history is broken, does not show changes diff. Done.
- version is zero. Done.
- update the modified by and modified at in currencies to use recorded terminology.
- We need to either make the columns configurable, or remove some less useful columns such as symbol, fraction etc.
- save new currency does not close dialog.
- can we refactor reload logic into common entity code.
Assorted fixes to accounts dialog code
- remove "grant administrative privileges" from details screen.
- old users remain online. Server should check sessions on start and update table.
- cannot tell who unlocked an account and when.
- do not use "old" for accounts who never logged in. Maybe "unused"?
- at present you can't tell if you asked for a password reset. We probably should use Status to mean: locked, unlocked, reset. And maybe "Age" or "Last Login" to mean: online, never, recent, old.
- add right-click menu with the context options from the toolbar.
- after reload, lock etc remember which line we were on and return to it.
- email updated message is truncated.
- user cannot lock it's own account.
Implement entity history in shell code
We need to add a command in shell to show entity history.
Notes:
- for the history diff we could use a simple unified diff format.
Consider adding an "entity waterfall" code
It would be nice to be able to see what entities have been added, deleted, modified, etc. This would work as a running commentary.
Notification of deletes code
At present it is easy to see new rows or modified rows in an entity dialog. It is not possible to see deleted rows. One way to do this is to preserve state from before reload by key. Any entities which are not present after reload can be shown as red. History then allows users to re-instate deleted entities.
Add REPL to Qt code
Users should be able to interact with the system directly via the REPL. Add a simple widget for this.
Add entity messages should not set modified by code
At present you can supply any modified by you want in the protocol messages. It makes more sense to say the message must have the modified by as the user logged in from the session.
Add heat map of user sessions code
Things to measure:
- duration of sessions (once we have session table).
- bytes sent/received per session (possibly 3-D plot?). Also good for anomaly detection.
- mine github for ideas.
Perform tests to check database connectivity code
We added some basic database monitoring but did not perform a lot of testing.
We should also generalise this so that if the database goes down after start we still perform some kind of retry logic.
See also this point from Gemini:
The new broadcast_all method appears to be unused in this pull request. The database status broadcast is implemented directly in
ores::comms::net::server::broadcast_database_statuswithout using the subscription_manager. If this method is not intended for future use, it could be removed to avoid dead code.
Add currencies update command to shell code
At present we can only add new currencies. We also need to be able to update. Also, adding currencies requires supplying all parameters.
Subscribe on reconnect fails code
Logs:
2025-12-13 01:14:35.108648 [DEBUG] [ores.comms.messaging.frame] Successfully deserialized frame subscribe_request (0x10) 2025-12-13 01:14:35.108680 [DEBUG] [ores.comms.net.connection] Successfully deserialized frame, type: subscribe_request (0x10) total size: 60 2025-12-13 01:14:35.108708 [DEBUG] [ores.comms.net.server_session] Received message type subscribe_request (0x10) 2025-12-13 01:14:35.108750 [DEBUG] [ores.comms.messaging.message_dispatcher] Dispatching message type subscribe_request (0x10) 2025-12-13 01:14:35.108811 [WARN] [ores.comms.service.auth_session_service] Authorization failed for subscribe_request (0x10) from 127.0.0.1:45498: not authenticated 2025-12-13 01:14:35.108842 [WARN] [ores.comms.messaging.message_dispatcher] Authorization denied for subscribe_request (0x10) from 127.0.0.1:45498 2025-12-13 01:14:35.108875 [ERROR] [ores.comms.net.server_session] Message dispatch failed: 10
Unsubscribe before logout code
On logout we see the following:
2025-12-11 22:45:54.559906 [INFO] [ores.accounts.messaging.accounts_message_handler] Successfully logged out account: 019a5e49-476c-70ce-9909-3887cae700e9 2025-12-11 22:45:54.559940 [DEBUG] [ores.comms.messaging.message_dispatcher] Successfully dispatched message, response type logout_response (0x200e) correlation_id=763 2025-12-11 22:45:54.559978 [DEBUG] [ores.comms.messaging.frame] Serialised frame logout_response (0x200e), size: 58 2025-12-11 22:45:54.560005 [DEBUG] [ores.comms.net.connection] Writing frame of size 58 type: logout_response (0x200e) sequence: 767 2025-12-11 22:45:54.560083 [DEBUG] [ores.comms.net.connection] Successfully wrote frame 2025-12-11 22:45:54.560113 [DEBUG] [ores.comms.net.session] Sent response for message type logout_request (0x200d) 2025-12-11 22:45:54.560137 [INFO] [ores.comms.net.session] Logout completed, closing connection 2025-12-11 22:45:54.560200 [ERROR] [ores.comms.net.session] Exception in notification writer: co_await: Operation canceled [system:125] 2025-12-11 22:45:54.560233 [DEBUG] [ores.comms.net.session] Notification writer coroutine ended 2025-12-11 22:45:54.560281 [INFO] [ores.comms.net.session] Unregistering session '127.0.0.1:38366' from subscription manager
We should probably unsubscribe before we logout.
Use events in comms and accounts code
Now we have an event bus, we should create events for:
- connect, disconnect, retry
- login, logout
Data in login info looks spurious code
We see stuff like this:
oresdb=> select * from login_info;
account_id | last_ip | last_attempt_ip | failed_logins | locked | last_login | online
--------------------------------------+-----------------+-----------------+---------------+--------+------------------------+--------
019a4439-be9e-798e-bf2f-927ca236f84c | 0.0.0.0 | 0.0.0.0 | 0 | 0 | 1969-12-31 23:00:00+01 | 0
019a3ba6-bd11-709b-b93d-fea9403d3d39 | 127.0.0.1 | 127.0.0.1 | 0 | 0 | 2025-10-31 19:03:48+00 | 1
019a4431-98f8-79ae-9fc8-f6a6e77a0490 | 192.168.1.100 | 192.168.1.100 | 0 | 0 | 2025-11-02 10:51:32+00 | 1
019a4431-9a7d-7b3d-a1cb-b9e02d44c804 | 0.0.0.0 | 192.168.1.100 | 1 | 0 | 1969-12-31 23:00:00+01 | 0
Also, we need a login timestamp and a logout timestamp so we can measure session duration.
Currencies displays when not connected code
At present we can display currencies even before we connect. This is probably ok but we should at least state we are not connected. Alternatively it should be disabled.
ores.qt is not using client session code
Refactor client manager to use it.
Add sound assets code
We need to be able to associate sounds to certain events such as disconnect, connect etc. Users need to be able to choose their own sounds.
Add a delete all button which deletes all currencies code
It is useful especially in test environments to be able to delete all entities before a re-import.
Notes:
- purge button.
Add screen in qt to exit bootstrap mode code
As per claude:
The create_initial_admin_request message type serves several important purposes that differentiate it from the CLI and REPL approaches:
Why We Need create_initial_admin_request
- Network-accessible bootstrap flow
- Allows remote initial setup from localhost without requiring shell/CLI access
to the server
- Enables a web-based "first-run" setup experience (like many web applications)
- The Qt client can provide a proper UI for initial admin account creation
- Localhost-only security enforcement
- The endpoint enforces that it can only be called from localhost (127.0.0.1 or
::1) at line projects/ores.accounts/src/messaging/accounts_message_handler.cpp:320
- Provides network-accessible bootstrap with security guarantees
- Prevents remote bootstrap attacks
- Atomic mode transition
- The endpoint both creates the admin AND exits bootstrap mode in a single atomic operation (lines 362-367 in accounts_message_handler.cpp)
- Guarantees consistency - can't create admin without exiting bootstrap mode
- Service-layer validation
- Goes through full service layer with password validation and business logic
- The CLI is essentially an "administrative backdoor" that bypasses the service
layer and writes directly to the database
- Protocol-compliant workflow
- Clients speaking the binary protocol need a proper bootstrap endpoint
- REPL create_account_request is blocked in bootstrap mode (by enforcement logic)
- create_initial_admin_request is the only endpoint that works in bootstrap mode
The Distinction
- CLI approach: Administrative backdoor, direct database access, bypasses all
service logic
- create_initial_admin_request: Proper network-accessible bootstrap flow with
localhost restriction and atomic mode transition
- REPL create_account_request: Normal account creation, blocked in bootstrap mode
The create_initial_admin_request enables a proper "first-run experience" for clients connecting via the network protocol, while maintaining security through localhost-only access.
Cli clean-ups code
- we still seem to support
--entity currencies. This should now be invalid. Done. - we are still exporting as JSON. We should instead allow CSV and XML exports only.
- add recipes for all commands.
- should be able to list all admin accounts.
- list command should support table output.
- is admin should be a bool:
--is-admin arg (=0) - add account duplicates logic. We should have a single service for this.
- split application into entities.
Add search to currencies code
It should be possible to filter the open currencies by a string. This should be any field. The user needs to know when the list has been filtered. Ideally we should have buttons at the top per field and filter using those. It should go back to database rather than just filter what is available in UI.
Links:
- GH pg_textsearch: Modern ranked text search for Postgres.
- Simple syntax: ORDER BY content <@> 'search terms'
- BM25 ranking with configurable parameters (k1, b)
- Works with Postgres text search configurations (english, french, german, etc.)
- Supports partitioned tables
- Goal: state-of-the-art performance and scalability
Footer
| Previous: Version Zero |