Tags

Tags give the ability to mark specific points in history as being important
  • 0.10.0

    Game networking stack: UDP reliability, DTLS transport, delta compression, IPv6, connection migration
  • 0.8.7

    OmniNet 0.8.7: Fix DB range-query first-row data loss (prompt.txt Bug 1)
    
    HIGH-severity bug filed by the daedalus project (consuming
    OmniNet) via prompt.txt 2026-07-10. Range-based
    db.query(sql) — returning a QueryResult — silently dropped
    the first row and yielded an empty row at index 0.
    
    Commits since 0.8.6:
      9f4c0b2  v0.8.7: Fix DB range-query first-row data loss
    
    Fix (src/db/Database.cpp QueryResult ctor): populate
    m_currentRow from the first step()'s row data. The Iterator
    ctor already copies m_currentRow from the QueryResult, so
    no changes needed there.
    
    Tests added (4):
    - database_query_range_first_row_not_empty
           The exact bug-report repro (a, b, c -> range yields
           a, b, c in order).
    - database_query_range_exactly_one_row
           Single-row edge case.
    - database_query_callback_still_works
           Pins the callback overload (daedalus's workaround).
    - database_query_range_early_break_unlocks_mutex
           Verifies early break still releases the db mutex.
    
    TDD: confirmed the bug reproduced before the fix by reading
    the new tests showed
      Assertion '__n < this->size()' failed
    in stl_vector.h when row[0] was accessed on the empty
    first-row vector.
    
    Verification:
    - omnet_test:         121/121 PASS (was 117/117)
    - 17 other suites:    all PASS (no regressions)
    - Full build:         clean, no new warnings
    
    Reported by: daedalus project via prompt.txt OMNINET-1
    (Bug 1) on 2026-07-10. Upgrade mandatory for any user of
    the range-based db.query(sql) API.
  • 0.8.6

    OmniNet 0.8.6: SSE unit-test improvements (catches OMNINET-1 regressions)
    
    The OMNINET-1 fix in v0.8.5 was correct, but the bug shouldn't
    have shipped in the first place. This release closes the test
    coverage gaps that let it through.
    
    Commits since 0.8.5:
      2dea92b  v0.8.6: Improve SSE unit tests to catch OMNINET-1-class
               regressions
    
    Changes:
    - Socket::isOpen() and Socket::waitForRead() now virtual
      (public-API-additive, no behavior change). Required for
      the FakeSocket unit-test mock.
    - New FakeSocket helper in tests/test_sse_streaming.cpp.
      Subclasses Socket, overrides recvAll/send/sendAll/close/
      isOpen/waitForRead. Drives recv behavior from the test.
    - 5 new runClientStream unit tests (no HTTP server needed):
        sse_runClientStream_continues_on_wouldblock
             OMNINET-1 regression catcher. PROVEN: fails with
             pre-v0.8.5 buggy code, passes with fix.
        sse_runClientStream_exits_on_real_eof
        sse_runClientStream_ignores_client_data
        sse_runClientStream_calls_remove_client_on_eof
        sse_runClientStream_fires_on_disconnect_callback
    - test_idle_connection sleep 500ms -> 1500ms. Now actually
      exercises idle-survival (was a sub-timeout sleep).
    
    Test status:
    - test_sse_streaming: 31/31 PASS (was 26/26)
    - omnet_test:         117/117 PASS (no regressions)
    - 16 other suites:    all PASS (no regressions)
    - Full build:         clean, no new warnings
  • 0.8.5

    OmniNet 0.8.5: Fix OMNINET-1 — SSEHandler kills idle connections
    
    HIGH-severity bug filed by the codemax team via prompt.txt
    (2026-07-10). Every OmniNet SSE endpoint died within
    cfg.keepAliveTimeout seconds (5s default, 1s in tests) because
    the SSE worker treated recvAll() returning empty as EOF when it
    was actually wouldBlock (SO_RCVTIMEO firing).
    
    Commits since 0.8.4:
      0fcd000  Fix OMNINET-1 (v0.8.5): SSEHandler kills idle connections
               within ~5s
      388a5e6  CodeMax.md: mark v0.8.5 task complete
    
    Root cause: HttpServer applies cfg.keepAliveTimeout as SO_RCVTIMEO
    on every accepted socket. After SSE upgrade, the worker thread
    inherits the timeout. Socket::recv() hides EAGAIN as return 0;
    Socket::recvAll() returns empty for return 0; the SSE loop treats
    empty as EOF. Worker exits every 5s, closes socket, browser
    reconnects.
    
    Fix: replace recvAll-based loop with select-based loop using
    Socket::waitForRead(1h). select() ignores SO_RCVTIMEO so the
    budget is independent. Shutdown still works via close-from-
    another-thread -> EBADF -> SocketException -> catch exit.
    
    Tests added (3):
    - sse_idle_survives_keepalive_timeout    (sleep 2.5s, verify alive)
    - sse_idle_with_broadcast_after_long_idle (sleep 3.5s, broadcast OK)
    - sse_eof_detection_still_works            (client close still works)
    
    Verification: test_sse_streaming 26/26 PASS (was 23/23),
    omnet_test 117/117 PASS, all 16 other suites PASS, full build
    clean, zero warnings.
  • 0.8.2

    OmniNet 0.8.2: HTTP Client Phase 7 (streaming) + EOF detection fixes
    
    Phase 7: streaming bodies
      Request streaming (chunked upload) via ClientRequest::set_body_writer()
      Response streaming via ClientResponseStream::read_some()/read_all()
      Client::send_streaming(req) returns ClientResponseStream
      Supports Transfer-Encoding: chunked, Content-Length, close-delimited
    
    EOF detection fix (post-Phase-7 bug)
      Socket::recvAll() is misleadingly named -- does a single recv()
      and returns empty on EAGAIN/EWOULDBLOCK (indistinguishable from EOF).
      HttpClient now drives its own recv() loop with waitForRead()
      timeout (50ms) so 'no data yet' vs 'peer closed' are correctly
      distinguished. recv() is wrapped in try/catch to handle abrupt
      peer closes.
    
    Files added/modified:
      src/http/HttpClient.h:    ClientResponseStream, ClientRequest::set_body_writer,
                                 Client::send_streaming
      src/http/HttpClient.cpp:  readAll uses recv() loop, recv() wrapped in
                                 try/catch, fetch() waits before recv
      tests/test_http_client.cpp: streaming test cases
    
    Tests:
      test_omnet_test:  110/110 PASS (no regressions)
      test_http_client streaming tests verified to work end-to-end
    
    Commit: 469f2b5
  • 0.8.1

    OmniNet 0.8.1: HTTP Client Phases 2-5
    
    Closes 4 deferred phases from the v0.8.0 plan. This is the
    release that unblocks CodeMax's HTTP client integration —
    Phase 3 (TLS) is the critical one since most production
    HTTPS APIs would otherwise be unusable.
    
    Phase 2 (async) — std::future<Result<..>> return type
    Phase 3 (TLS)   — HTTPS via existing net::TLSSocket
    Phase 4 (redirects) — automatic 3xx following
    Phase 5 (timeouts) — connect/request_timeout + stop_token
    
    Plus: chunked transfer-encoding (Transfer-Encoding: chunked)
    is now parsed correctly in parseResponse().
    
    Commit: 36d07de
    Tests: test_http_client 28/28 PASS, test_omnet_test 110/110 PASS
    
    Not yet shipped (deferred per plan):
      Phase 6: bearer/basic auth, cookie jar
      Phase 7: streaming bodies (req/resp)
      Phase 8: retry policy, happy-eyeballs connect
  • 0.8.0

    OmniNet 0.8.0: HTTP Client (Phase 1 — minimal sync client)
    
    NEW MODULE: OmniNet::HTTP::Client
    
    Adds the first in-tree HTTP client to OmniNet (was server-only).
    Phased delivery (see CodeMax.md for the full Phase 2-8 roadmap);
    this release ships Phase 1 only:
    
      - OmniNet::HTTP::Client::get_instance() singleton
      - ClientRequest / ClientResponse / ClientConfig / ClientError types
      - send_blocking(req) / send_blocking(req, cfg)
      - HTTP/1.1 GET and POST over plain Socket
      - Case-insensitive header lookups
      - URL parsing: scheme://host[:port]/path[?query]
      - https:// returns UnsupportedProtocol (Phase 3)
    
    Deferred to future tags (0.8.1, 0.8.2, ...):
      - Phase 2: async std::future return type
      - Phase 3: TLS (reuse net::TLSSocket + TLSContext::createClient)
      - Phase 4: automatic 3xx redirect following
      - Phase 5: connect/request timeouts + std::stop_token cancellation
      - Phase 6: bearer/basic auth + cookie jar
      - Phase 7: chunked streaming bodies (req/resp)
      - Phase 8: retry policy + happy-eyeballs connect
    
    Commits:
      c6b5c02  Add OmniNet HTTP Client (Phase 1: minimal sync client)
    
    Tests:
      test_http_client: 20/20 PASS
      test_omnet_test:  110/110 PASS (no regressions)
      Full build clean, zero warnings.
  • 0.7.7

    OmniNet 0.7.7: Add typed queryInt/queryDouble accessors
    
    API improvement based on feedback from ApolloCore integration
    (prompt.txt 2026-06-27, Bug 4).
    
    New methods on QueryParams (HttpRequest.h/cpp):
      std::optional<int> queryInt(std::string_view key) const;
      int queryInt(std::string_view key, int defaultValue) const;
      std::optional<double> queryDouble(std::string_view key) const;
      double queryDouble(std::string_view key, double defaultValue) const;
    
    Before:
      auto v = req.query().get("limit");
      if (v) int n = std::stoi(std::string(*v));  // awkward
    
    After:
      int n = req.query().queryInt("limit", 100);  // with default
      auto m = req.query().queryDouble("ratio");  // optional
    
    Trailing non-digit chars are rejected (e.g., "10x" -> nullopt,
    not 10). Parse errors caught via std::stoi/std::stod try/catch
    and return nullopt.
    
    Other bugs from the same prompt.txt triage:
      #1 SQLiteDB::step - in ApolloCore repo, NOT OmniNet.
      #2 delete_()     - not a bug (C++ keyword constraint).
      #3 setBody       - already fixed (string_view overload exists).
      #5 setStatus     - already fixed (int overload exists).
      #6 shutdown      - not a bug (Server::stop() exists).
      #7-#8           - vague / out of scope.
      #9-#13          - llama.cpp / ApolloCore, NOT OmniNet.
    
    Commit: ba9b2cf
    Build verified clean. All 110 tests pass (no regressions).
  • 0.7.6

    OmniNet 0.7.6: Fix codemax Bug 9 (wss:// ctor crash)
    
    Bug 9 (CRITICAL regression from 0.7.5):
      WebSocket::Connection(url) for wss:// never called
      m_socket->connect() before performClientHandshake(), causing
      a hard process abort with:
    
        *** bit out of range 0 - FD_SETSIZE on fd_set ***: terminated
    
      TLSContext::createSocket() returns a TLSSocket with
      m_fd == INVALID_SOCKET. TLSSocket::recvAll() reaches
      FD_SET(INVALID_SOCKET, ...) which writes out of bounds
      in glibc's __FD_SET macro.
    
    Fix: Add m_socket->connect(parsed->host, parsed->port) after
    the setALPNProtocols call in the wss:// branch of
    Connection::Connection(url). Mirrors what createConnectedSocket()
    does for the plain ws:// branch.
    
    Commit: df07b4f
    
    Affects: any user of WebSocket::Connection(url) with a wss:// URL.
            (Hard process abort — no exception to catch.)
    
    Does NOT affect:
      - WebSocket::Connection(unique_ptr<Socket>, ...) — server-side
      - WebSocket::Connection(url) with ws:// (plain text)
    
    Upgrade: MANDATORY if you adopted wss:// in 0.7.5. The codemax
             workaround (reject wss:// upfront) is no longer needed.
             Otherwise optional.
    
    Build verified clean. All existing tests pass (no regressions).
  • 0.7.5

    OmniNet 0.7.5: 2 codemax WebSocket client fixes
    
    Two bugs in WebSocket::Connection client API fixed:
    
    Bug 8 (HIGH): Connection::receive() loops forever
      - Previously read exactly 2 bytes per recv() and passed them to
        FrameCodec::decode(). For any payload > 0 bytes, decode()
        couldn't satisfy its need for the full frame header + payload
        length + payload, so it returned nullopt, the loop continued,
        and the next recv() ate the start of the payload as if it were
        a header - hanging forever on every multi-byte frame.
      - Fix: mirror the server-side readLoop() pattern. Buffer bytes
        into a vector, keep calling FrameCodec::decode() until we have
        a complete frame, return it.
      - Commit: cacca33
    
    Bug 7 (MED): Connection::Connection(url) ignores wss://
      - Handshake::parseUrl() correctly set parsed->secure = true for
        wss:// URLs, but the Connection ctor ignored it and always
        used createConnectedSocket() which returns a plain Socket.
        The client sent a plaintext upgrade, the server returned 426
        Upgrade Required, and the handshake failed with an opaque error.
      - Fix: branch on parsed->secure. For ws://, use
        createConnectedSocket() (existing). For wss://, use
        TLSContext::createClient()->createSocket() with SNI from the
        host and ALPN advertising {'http/1.1'}.
      - Commit: 8cdf9e3
    
    Affects: any user of WebSocket::Connection(url) client API (both
            ws:// and wss://). Particularly important for codemax's
            websocket_connect tool which now works end-to-end against
            wss://echo.websocket.events and similar.
    Does NOT affect: server-side WebSocket::Connection usage (already
            worked via different code path).
    
    Build verified clean. All existing tests pass (no regressions).
  • 0.7.4

    OmniNet 0.7.4: CRITICAL — Fix TLSContext::createSocket() double-free
    
    This is a hotfix release. v0.7.2 and v0.7.3 had a regression
    that caused 'free(): double free' abort on every HTTPS request
    when using the new TLSContext::createClient()->createSocket()
    factory pattern.
    
    Root cause: Both ~TLSSocket() (via cleanupContext()) AND
    ~TLSContext() called SSL_CTX_free() on the same SSL_CTX pointer.
    When the socket was created via createSocket(), the socket's
    m_ctx aliased the context's m_ctx (no copy, just a pointer).
    When both destructors ran, SSL_CTX_free was called twice.
    
    Fix: Track ownership via the m_context shared_ptr member.
    In cleanupContext(), only call SSL_CTX_free(m_ctx) when m_context
    is null (the socket owns its own context). When m_context is
    non-null, the shared TLSContext owns the SSL_CTX and is the
    sole owner responsible for freeing it. The socket just drops
    its alias (sets m_ctx = nullptr).
    
    Commit: c994cf2
    Affects: any user of TLSContext::createClient()->createSocket()
             or TLSContext::createServer()->createSocket()
    Does NOT affect: TLSSocket constructed directly (default ctor,
                      fd ctor, or std::unique_ptr<Socket> ctor) — those
                      still own their SSL_CTX and free it in ~TLSSocket.
    
    Upgrade: MANDATORY if you use TLSContext::createSocket() in 0.7.2/0.7.3.
             Otherwise optional (0.7.2/0.7.3 is fine for the TLSSocket
             default-constructor path).
    
    Build verified clean. All existing tests pass (no regressions).
  • 0.7.3

    OmniNet 0.7.3: Documentation updates for v0.7.1 + v0.7.2
    
    No code changes. Docs-only release covering the 11 bug fixes
    shipped in 0.7.1 and 0.7.2.
    
      - docs/migration/v0.7.0-to-v0.7.2.md (NEW)
        Full migration guide covering both intermediate releases.
        Compatibility matrix, recommended actions with before/after
        code, security defaults, performance notes.
    
      - docs/api/http-server.md
        Updated CORS Setup with the new allowlist behavior table.
        Updated CORS Headers with per-request Origin logic.
        Added new TLS Client Connections section covering
        TLSContext::createClient/createServer/createSocket,
        security defaults, sharing contexts, ALPN usage.
    
      - docs/api/index.md
        Added What's New section linking to migration guide.
        Summarizes v0.7.2 TLS fixes, v0.7.1 bug fixes,
        v0.7.0 WebSocket fix.
    
      - docs/api/websocket.md
        Added note about registerProtocolSwitch() atomic
        registration (the v0.7.0 fix for TOCTOU race and
        single-shot erase).
    
    README.md untouched — it has no specific class/version
    references, and detail belongs in the API docs.
    
    Build verified clean (no code changes anyway).
  • 0.7.2

    OmniNet 0.7.2: 5 codemax TLS bug fixes
    
    Commits since 0.7.1:
    
      45cae5b Fix Bug 3: TLSSocket::readLine() overrides base
        Previously, calling readLine() on a TLSSocket used the inherited
        Socket::readLine() which calls raw ::recv() on the underlying fd,
        bypassing SSL_read. The result was raw TLS ciphertext (the
        record header bytes) which looked like 'malformed status line'
        to text-line parsers. Override readLine() to use the overridden
        recv() (which goes through SSL_read), one byte at a time.
    
      fc97be9 Fix Bug 4: load system CA + SSL_VERIFY_PEER by default
        Previously, initContext() created an SSL_CTX but didn't load any
        CA certs and didn't set a verify mode. OpenSSL's default is
        SSL_VERIFY_NONE, and setVerifyMode(PEER) on its own doesn't
        load the CA store. So setVerifyMode(PEER) + connect() would
        silently accept forged certificates. Fix: in initContext(),
        for client contexts, call SSL_CTX_set_default_verify_paths()
        and SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER, nullptr).
        Callers can still override via setVerifyMode(NONE).
    
      913e44b Fix Bug 1 + 5: implement TLSContext::createClient/createSocket
        Previously, TLSContext::createClient() and createSocket() were
        declared but never defined - link failed with 'undefined
        reference'. createServer() was also unimplemented. Made
        TLSContext inherit std::enable_shared_from_this so createSocket()
        can use shared_from_this(). Implemented createServer(),
        createClient(), createSocket(), and the private ctor + dtor.
        Added TLSSocket(shared_ptr<TLSContext>) constructor and
        setContext() helper for adopting a shared context.
    
      d508494 Fix Bug 2: setALPNProtocols() actually sets ALPN
        Previously, setALPNProtocols() was a no-op stub. Without ALPN,
        modern HTTPS servers (Cloudflare, GitHub, Google) default to
        HTTP/2 which has a binary wire format incompatible with text-line
        HTTP/1.1 parsers - resulting in garbage status lines like
        'PRI * HTTP/2.0'. Fix: build the ALPN wire format (RFC 7301)
        and call SSL_CTX_set_alpn_protos() on m_ctx. getALPNProtocol()
        was already implemented correctly.
    
    Plus: CodeMax.md updates tracking the work.
    
    Tests: test_omnet_test 110/110 PASS, test_sync_http_server 18/18 PASS.
    Full build clean, zero warnings. All 5 codemax integration bugs fixed.
  • 0.7.1

    OmniNet 0.7.1: 6 prompt.txt (2026-06-19) bug fixes
    
    Commits since 0.7.0:
    
      42aa12b Fix BUG-1: IntrusionDetector self-deadlock
        Root cause: checkAndBlock() held m_mutex and called isBlocked()
        which also tried to lock — std::mutex is not recursive.
        Fix: split isBlocked() into isBlocked() (locks) and
        isBlockedUnlocked() (caller holds lock).
    
      e49f293 Fix BUG-2: CORS per-origin allowlist
        Was: config string emitted verbatim as the
        Access-Control-Allow-Origin header (CSV — rejected by all browsers).
        Now: parse CSV into std::unordered_set at enableCORS() time;
        per-request lookup of Origin header, echo back with
        Vary: Origin and Access-Control-Allow-Credentials: true.
        "*" is a special case for allow-all.
    
      BUG-3 was already fixed in v0.6.0 — no commit needed.
    
      d23c8af Fix BUG-4: remove dead config sections
        omninet-init was writing security.*, dashboard.*, and
        intrusion.* sections that the server never reads.
        Removed the three dead sections from the generated config.
    
      34bf1c8 Fix BUG-5: centralize IntrusionDetector schema
        Extracted CREATE TABLE / CREATE INDEX statements from
        the constructor into a public static method schemaSQL().
        The constructor calls it; behavior unchanged.
        Schema is now in one place for migrations / introspection.
    
      1efc02b Fix BUG-6: remove dead defaultPatterns()
        defaultPatterns() and SuspiciousPattern were declared
        but never called. Actual rules live in
        IntrusionRules::allDefaultRules(). Dead code removed.
    
    Plus: CodeMax.md updates (6764a23, 9ef3121) tracking the work.
    
    Tests: test_omnet_test 110/110 PASS, test_sync_http_server
    18/18 PASS. Full build clean, zero warnings.
  • 0.7.0

    OmniNet 0.7.0: Hive-Agent Features + WebSocket Upgrade Fix + Version Macros
    
    All 6 features from the hive-agent feature request (prompt.txt)
    are now complete, plus the WebSocket HTTP→WS upgrade handoff fix
    from the 2026-06-14 update, plus a version-macros fix so the
    CLI tools correctly report v0.7.0 instead of the v0.1.0 fallback.
    
    - Phase 11a: Server::boundPort() — actual bound port via getsockname()
    - Phase 11b: WebSocket::Server::broadcast() / fan-out API
      (addClient, removeClient, sendTo, clients, connectionCount)
    - Phase 11c: Server::onReady(uint16_t boundPort) lifecycle callback
    - Phase 11d: Request::authHeader() / bearerToken() / traceId() / requestId()
    - Phase 11e: Router typed body parsing (JsonHandler overloads for
      get/post/put/patch/delete_/head/options)
    - Bonus (74d9418): full HTTP→WS upgrade handoff via PostUpgradeCallback
      mechanism, so the WebSocket broadcast API is now reachable from
      real client connections.
    
    - Root CMakeLists.txt now uses ${DETECTED_MAJOR} (set by the
      git-tag parser) instead of ${PROJECT_VERSION_MAJOR} (set by
      project() AFTER our compile_definitions call, so it overrode
      our values).
    - tools/tools_common/CMakeLists.txt had two duplicate
      compile_definitions blocks that were never actually removed
      despite a v0.6.0 comment claiming they were. The v0.6.0
      fix comment was also wrong: PUBLIC compile_definitions on
      OmniNet do NOT transitively propagate through static-library
      dependents (tools_common), so tools were always seeing the
      fallback values from version.h (0.1.0) instead of the real
      version.
    - Removed a duplicate version-print line in root CMakeLists.txt.
    
    - Response::build() suppresses Content-Length for 101/204/304
      (RFC 7230 §3.3.2, RFC 6455 §1.3)
    - Connection server-side ctor sets m_open = true
      (was default false, broke addClient)
    - Connection::close() checks for null socket (fixes mock segfault)
    - Self-pipe trick for instant accept-loop shutdown (replaces 100ms poll)
    - Connection timeouts (SO_RCVTIMEO) now actually applied to socket
    - Content-Length auto-set in Response::build() (was missing in v0.6.0)
    
    - test_websocket_echo (new, 74d9418): 2/2 PASS
    - test_websocket_broadcast: 10/10 PASS
    - test_sync_http_server: 18/18 PASS (incl. 9 new boundPort/onReady tests)
    - All other tests: PASS
    - Full build: clean, zero warnings, all 40+ targets compile
    - All 10 CLI tools report v0.7.0