마인드의 눈 패브릭
발행: (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
| Component | Description |
|---|---|
| ledger / Append‑only log | 불변 엔트리, 파일 기반 |
| state | 결정적 상태 머신 + 전이 규칙 |
| graph | 엔드포인트, capability, 도달 가능성 계산 |
| events | 프로세스 내부 pub/sub 버스(데몬의 신경계) |
| hunts | 제약 조건 하에 도달 가능한 엔드포인트를 탐색하는 플래너 |
| daemon | 메인 루프 + API 표면(HTTP/gRPC는 추후; Phase 1은 HTTP 사용) |
Phase 1에서 collapse = 원자적 커밋:
- 입력(event / command) 수신
- 검증
- 결과 계산(state + graph 업데이트)
- 원장에 하나의 엔트리 기록(append‑only)
- 내부 이벤트 발생
원장 커밋 없이 변형은 일어나지 않습니다.
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 및 재생이 쉽습니다.
| Field | Description |
|---|---|
id | 단조 증가 정수 |
ts_ms | 타임스탬프(밀리초) |
kind | "event" |
payload | JSON 객체(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)
Header
#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 등)은 동일한 클린‑코드 규칙을 따르며, 이후 단계에서 확장될 예정입니다.