Skip to content

securecrdt/SecureCrdtRegistry.hpp

Instance registry mapping a base_key pattern to {signer-set source, required signature count, ISignedCRDTData factory}, resolvable at startup/runtime by key. Each SecureCrdt owns an independent instance so in-process nodes cannot replace one another's policy. More...

Namespaces

Name
sgns
sgns::peerregistry
sgns::securecrdt

Classes

Name
struct sgns::securecrdt::SignerSetSnapshot
Snapshot of an authorized signer set and its required signature count, as produced by an injected SignerSetSource.
struct sgns::securecrdt::CandidateAuthorizationSnapshot
struct sgns::securecrdt::CandidateDomainEntry
struct sgns::securecrdt::SecureCrdtRegistryEntry
Policy entry describing how a registered key pattern is verified and instantiated.
class sgns::securecrdt::SecureCrdtRegistry
Thread-safe registry resolving a CRDT key to its policy entry.

Types

Name
using std::function< outcome::result< SignerSetSnapshot >(const std::string &base_key)> SignerSetSource
Injectable callback resolving the current authorized signer set for a given base_key. NOT hard-wired to TrustedPeerRegistry (which does not exist until Phase 10) - tests inject a fixed-list lambda.
using std::function< outcome::result< CandidateAuthorizationSnapshot >()> CandidateAuthorizationSource

Detailed Description

Instance registry mapping a base_key pattern to {signer-set source, required signature count, ISignedCRDTData factory}, resolvable at startup/runtime by key. Each SecureCrdt owns an independent instance so in-process nodes cannot replace one another's policy.

Date: 2026-07-23 Henrique A. Klein ([email protected])

Types Documentation

using SignerSetSource

using sgns::securecrdt::SignerSetSource = typedef std::function<outcome::result<SignerSetSnapshot>( const std::string &base_key )>;

Injectable callback resolving the current authorized signer set for a given base_key. NOT hard-wired to TrustedPeerRegistry (which does not exist until Phase 10) - tests inject a fixed-list lambda.

using CandidateAuthorizationSource

using sgns::securecrdt::CandidateAuthorizationSource = typedef std::function<outcome::result<CandidateAuthorizationSnapshot>()>;

Return SecureCrdt::Error::CANDIDATE_AUTHORIZATION_PENDING when local trust prerequisites are missing, so incoming approvals can be retried safely.

Source code

#ifndef SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP
#define SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP

#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <regex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include "outcome/outcome.hpp"
#include "securecrdt/ISignedCRDTData.hpp"
#include "securecrdt/SecureCrdtCandidate.hpp"

namespace sgns::peerregistry
{
    class PeerRegistry; // complete type not needed here - association only (D-04);
                        // the adaptation helper lives in peerregistry/PeerRegistry.hpp
                        // to avoid an include cycle.
} // namespace sgns::peerregistry

namespace sgns::securecrdt
{
    struct SignerSetSnapshot
    {
        std::vector<std::string> signer_set;
        uint64_t                 required_signatures = 0;
    };

    using SignerSetSource = std::function<outcome::result<SignerSetSnapshot>( const std::string &base_key )>;

    struct CandidateAuthorizationSnapshot
    {
        uint16_t                 network_id   = 0;
        CandidateKind            kind         = CandidateKind::TrustPolicy;
        uint64_t                 next_version = 0;
        std::string              expected_previous_hash;
        std::string              authorizing_policy_hash;
        std::vector<std::string> authorized_signers;
    };

    using CandidateAuthorizationSource = std::function<outcome::result<CandidateAuthorizationSnapshot>()>;

    struct CandidateDomainEntry
    {
        std::string                  domain;
        CandidateKind                kind = CandidateKind::TrustPolicy;
        CandidateAuthorizationSource authorization_source;
        const void                  *owner_token = nullptr;
    };

    struct SecureCrdtRegistryEntry
    {
        std::string                                       key_pattern;
        SignerSetSource                                   signer_set_source;
        std::function<std::shared_ptr<ISignedCRDTData>()> make_instance;
        std::regex                                        compiled_pattern;
        const void                                          *owner_token = nullptr;
        std::shared_ptr<sgns::peerregistry::PeerRegistry>    peer_registry;
    };

    class SecureCrdtRegistry
    {
    public:
        bool Register( const std::string &key_pattern, SecureCrdtRegistryEntry entry )
        {
            entry.key_pattern      = key_pattern;
            entry.compiled_pattern = std::regex( "/?" + key_pattern + "(/sig/[^/]+)?" );
            {
                // Unlink any replaced entry WITHOUT destroying it while the
                // registry mutex is held: a replaced entry's peer_registry may
                // own the last reference to a PeerRegistry whose destructor
                // re-enters Unregister() -> UnregisterIf() (destruction
                // re-entrancy; std::shared_mutex is not recursive).
                std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
                auto                                replaced = registry_.extract( key_pattern );
                lock.unlock();
            } // replaced node (if any) destroyed here, mutex released
            std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
            return registry_.insert_or_assign( key_pattern, std::move( entry ) ).second;
        }

        bool RegisterIfAbsent( const std::string &key_pattern, SecureCrdtRegistryEntry entry )
        {
            entry.key_pattern      = key_pattern;
            entry.compiled_pattern = std::regex( "/?" + key_pattern + "(/sig/[^/]+)?" );
            bool inserted = false;
            {
                std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
                // find-then-emplace under ONE continuous lock hold: emplace
                // cannot lose the race, so the moved entry is never destroyed
                // under the mutex (mirror of Register/UnregisterIf's
                // extract-then-destroy destruction-reentrancy safety -- a
                // failed insert's caller-owned entry copy is destroyed after
                // the lock released).
                if ( registry_.find( key_pattern ) == registry_.end() )
                {
                    inserted = registry_.emplace( key_pattern, std::move( entry ) ).second;
                }
            } // lock released; a rejected entry copy is destroyed after this point
            return inserted;
        }

        bool UnregisterIf( const std::string &key_pattern, const void *expected_token )
        {
            std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
            auto                                it = registry_.find( key_pattern );
            if ( it != registry_.end() && it->second.owner_token == expected_token )
            {
                // Unlink without destroying under the lock: the entry's
                // peer_registry may own the last PeerRegistry reference, whose
                // destructor re-enters Unregister() -> UnregisterIf()
                // (destruction re-entrancy; std::shared_mutex is not recursive).
                auto node = registry_.extract( it );
                lock.unlock();
                return true;
            } // node destroyed here, mutex released
            return false;
        }

        std::optional<SecureCrdtRegistryEntry> Resolve( const std::string &key ) const
        {
            std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
            for ( const auto &[pattern, entry] : registry_ )
            {
                if ( std::regex_match( key, entry.compiled_pattern ) )
                {
                    return entry;
                }
            }
            return std::nullopt;
        }

        std::vector<SecureCrdtRegistryEntry> AllEntries() const
        {
            std::shared_lock<std::shared_mutex>  lock( registry_mutex_ );
            std::vector<SecureCrdtRegistryEntry> entries;
            entries.reserve( registry_.size() );
            for ( const auto &[pattern, entry] : registry_ )
            {
                entries.push_back( entry );
            }
            return entries;
        }

        bool RegisterCandidateDomain( const std::string &domain, CandidateDomainEntry entry )
        {
            entry.domain = domain;
            std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
            return candidate_domains_.emplace( domain, std::move( entry ) ).second;
        }

        void UnregisterCandidateDomainIf( const std::string &domain, const void *expected_token )
        {
            std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
            auto                                it = candidate_domains_.find( domain );
            if ( it != candidate_domains_.end() && it->second.owner_token == expected_token )
            {
                candidate_domains_.erase( it );
            }
        }

        std::optional<CandidateDomainEntry> ResolveCandidateDomain( const std::string &domain ) const
        {
            std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
            const auto                          it = candidate_domains_.find( domain );
            return it == candidate_domains_.end() ? std::nullopt : std::optional<CandidateDomainEntry>( it->second );
        }

        std::vector<CandidateDomainEntry> AllCandidateDomains() const
        {
            std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
            std::vector<CandidateDomainEntry>   entries;
            entries.reserve( candidate_domains_.size() );
            for ( const auto &[domain, entry] : candidate_domains_ )
            {
                entries.push_back( entry );
            }
            return entries;
        }

    private:
        std::unordered_map<std::string, SecureCrdtRegistryEntry> registry_;
        std::unordered_map<std::string, CandidateDomainEntry>    candidate_domains_;
        mutable std::shared_mutex                                registry_mutex_;
    };
} // namespace sgns::securecrdt

#endif // SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP

Updated on 2026-10-07 at 18:59:02 +0000