마인드의 눈 패브릭

발행: (2025년 12월 13일 오후 05:26 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

Phase 1 — C++ Sovereign Kernel Skeleton (Daemon‑First)

Goal – 실행 중인 C++ 데몬을 제공하여 다음을 수행하도록 합니다:

  • 이벤트 수신
  • capability 그래프(엔드포인트 + 엣지) 유지
  • 최소 상태 커널 실행(결정적 전이)
  • append‑only 원장 기록(출처 + 재생 가능)
  • 첫 번째 “hunt”(도달 가능성에 대한 그래프 탐색) 실행

Non‑goals (Phase 1)

  • LLM 통합
  • MindScript 파싱/컴파일
  • 분산 다노드 합의
  • 고급 영속성 엔진(스토리지는 깨끗하고 교체 가능하게 설계)

Core Concepts

ComponentDescription
ledger / Append‑only log불변 엔트리, 파일 기반
state결정적 상태 머신 + 전이 규칙
graph엔드포인트, capability, 도달 가능성 계산
events프로세스 내부 pub/sub 버스(데몬의 신경계)
hunts제약 조건 하에 도달 가능한 엔드포인트를 탐색하는 플래너
daemon메인 루프 + API 표면(HTTP/gRPC는 추후; Phase 1은 HTTP 사용)

Phase 1에서 collapse = 원자적 커밋:

  1. 입력(event / command) 수신
  2. 검증
  3. 결과 계산(state + graph 업데이트)
  4. 원장에 하나의 엔트리 기록(append‑only)
  5. 내부 이벤트 발생

원장 커밋 없이 변형은 일어나지 않습니다.


Project Structure

mindseye-fabric/
├─ README.md
├─ LICENSE
├─ CMakeLists.txt
├─ docs/
│  └─ 01_phase1_kernel.md
├─ src/
│  ├─ main.cpp
│  ├─ daemon/
│  │  ├─ server.hpp
│  │  ├─ server.cpp
│  │  ├─ routes.hpp
│  │  └─ routes.cpp
│  ├─ core/
│  │  ├─ types.hpp
│  │  ├─ time.hpp
│  │  └─ result.hpp
│  ├─ ledger/
│  │  ├─ ledger.hpp
│  │  ├─ ledger.cpp
│  │  └─ entry.hpp
│  ├─ state/
│  │  ├─ state.hpp
│  │  ├─ state.cpp
│  │  └─ transitions.hpp
│  ├─ graph/
│  │  ├─ graph.hpp
│  │  ├─ graph.cpp
│  │  ├─ endpoint.hpp
│  │  └─ capability.hpp
│  ├─ events/
│  │  ├─ bus.hpp
│  │  └─ bus.cpp
│  └─ hunts/
│     ├─ hunt.hpp
│     └─ hunt.cpp
├─ third_party/
│  └─ httplib.h
└─ tests/
   └─ test_smoke.cpp

각 폴더는 커널의 “기관”이며, 혼합 목적의 “utils” 디렉터리는 없습니다.


CMake Configuration

cmake_minimum_required(VERSION 3.20)
project(mindseye_fabric LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(mindseye_fabric
  src/main.cpp
  src/daemon/server.cpp
  src/daemon/routes.cpp
  src/events/bus.cpp
  src/ledger/ledger.cpp
  src/state/state.cpp
  src/graph/graph.cpp
  src/hunts/hunt.cpp
)

target_include_directories(mindseye_fabric PRIVATE
  src
  third_party
)

# Good defaults
if(MSVC)
  target_compile_options(mindseye_fabric PRIVATE /W4)
else()
  target_compile_options(mindseye_fabric PRIVATE -Wall -Wextra -Wpedantic)
endif()

Core Types (src/core/types.hpp)

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace me {

using u64 = std::uint64_t;
using i64 = std::int64_t;

struct Error {
  std::string code;
  std::string message;
};

template 
using Result = std::variant;

inline bool ok(const auto& r) {
  return std::holds_alternative(r))>>(r);
}

} // namespace me

Core Time (src/core/time.hpp)

#pragma once
#include 
#include "core/types.hpp"

namespace me {

inline u64 now_ms() {
  using namespace std::chrono;
  return static_cast(duration_cast(system_clock::now().time_since_epoch()).count());
}

} // namespace me

Ledger Entry Format

엔트리는 한 줄 JSON(NDJSON) 형태이며, tail 및 재생이 쉽습니다.

FieldDescription
id단조 증가 정수
ts_ms타임스탬프(밀리초)
kind"event"
payloadJSON 객체(Phase 1에서는 문자열화)
prev_hash이전 엔트리의 해시
hash현재 엔트리의 해시(무결성 체인)

src/ledger/entry.hpp

#pragma once
#include 
#include "core/types.hpp"

namespace me {

struct LedgerEntry {
  u64 id = 0;
  u64 ts_ms = 0;
  std::string kind;
  std::string payload_json;
  std::string prev_hash;
  std::string hash;
};

} // namespace me

Ledger Implementation (src/ledger/ledger.hpp / .cpp)

#pragma once
#include 
#include 
#include 
#include "ledger/entry.hpp"
#include "core/types.hpp"

namespace me {

class Ledger {
public:
  explicit Ledger(std::string path);

  Result append(std::string kind, std::string payload_json);

  std::optional last() const;
  u64 next_id() const;

private:
  std::string path_;
  mutable std::mutex mu_;
  u64 next_id_{1};
  std::optional last_;

  static std::string sha256_hex(std::string_view data); // stub for Phase 1
  static std::string compute_hash(const LedgerEntry& e);
};

} // namespace me

Source

#include "ledger/ledger.hpp"
#include "core/time.hpp"
#include 

namespace me {

static std::string fake_hash(std::string_view s) {
  // Phase 1 placeholder – replace with real SHA‑256 later.
  std::hash h;
  return std::to_string(h(s));
}

Ledger::Ledger(std::string path) : path_(std::move(path)) {
  // Start fresh if the file does not exist.
  std::ifstream in(path_);
  if (!in.good()) return;

  // Minimal restore: read the last line only (fast path).
  std::string line, last_line;
  while (std::getline(in, line))
    if (!line.empty()) last_line = line;

  // Phase 1: keep next_id_ conservative.
  if (!last_line.empty()) next_id_ = 1000; // placeholder
}

std::optional Ledger::last() const {
  std::scoped_lock lk(mu_);
  return last_;
}

u64 Ledger::next_id() const {
  std::scoped_lock lk(mu_);
  return next_id_;
}

std::string Ledger::sha256_hex(std::string_view data) {
  return fake_hash(data);
}

std::string Ledger::compute_hash(const LedgerEntry& e) {
  std::ostringstream ss;
  ss  Ledger::append(std::string kind, std::string payload_json) {
  std::scoped_lock lk(mu_);
  LedgerEntry e;
  e.id = next_id_++;
  e.ts_ms = now_ms();
  e.kind = std::move(kind);
  e.payload_json = std::move(payload_json);
  e.prev_hash = last_ ? last_->hash : "GENESIS";
  e.hash = compute_hash(e);

  std::ofstream out(path_, std::ios::app);
  if (!out.good())
    return Error{"LEDGER_IO", "Failed to open ledger file for append"};

  // NDJSON line
  out 

State Header (src/state/state.hpp)

#include "core/types.hpp"

namespace me {

enum class State : u64 {
  PAUSE = 0,
  STRESS = 1,
  LOOP = 2,
  TRANSMIT = 3,
  COLLAPSE = 4
};

inline std::string to_string(State s) {
  switch (s) {
    case State::PAUSE:    return "PAUSE";
    case State::STRESS:   return "STRESS";
    case State::LOOP:     return "LOOP";
    case State::TRANSMIT: return "TRANSMIT";
    case State::COLLAPSE: return "COLLAPSE";
  }
  return "UNKNOWN";
}

struct Transition {
  State from;
  State to;
  std::string reason;
};

class StateKernel {
public:
  State current() const { return current_; }

  // Phase 1: simple rule set. Later: constraints + costs + guards.
  Transition apply_event(std::string_view event_type);

private:
  State current_{State::PAUSE};
};

} // namespace me

Source (src/state/state.cpp)

#include "state/state.hpp"

namespace me {

Transition StateKernel::apply_event(std::string_view event_type) {
  // Deterministic, minimal mapping for Phase 1.
  if (event_type == "ingest") {
    State prev = current_;
    current_ = State::LOOP;
    return {prev, current_, "ingest -> LOOP"};
  }
  if (event_type == "pressure") {
    State prev = current_;
    current_ = State::STRESS;
    return {prev, current_, "pressure -> STRESS"};
  }
  // Default: no state change.
  return {current_, current_, "no transition"};
}

} // namespace me

나머지 모듈(graph, events, hunts, daemon server, routes 등)은 동일한 클린‑코드 규칙을 따르며, 이후 단계에서 확장될 예정입니다.

Back to Blog

관련 글

더 보기 »