전체 문서
이 페이지 목차
Traffic SQLite와 파일 저장 계약
traffic recorder의 SQLite 일반 table 3개, 선택적 FTS5 virtual table 1개와 body/blob/file 경계를 설명한다.
각 객체의 exact production Go symbol, SQL verb와 query 조건은 DB access matrix에서 역추적한다.
기준 · core
db/schema.sql은 PostgreSQL 49개 table·최종 column 합집합 520개·명시 index 97개·function 4개·trigger 22개다. startup 보조 ledgerllm_records와llm_usage가 2개 table·31개 column·5개 index를 더해 선언 총계는 51개 table·551개 column·102개 index다.Ensure*Table실패는 로그 후 계속되므로 보조 ledger 존재는 core startup 보장이 아니다. Traffic은 별도 SQLite 3개 일반 table·FTS5 virtual table 1개·index 6개다. 아래 DDL은 고정 source revision의 선언이며 실제 운영 DB 적용 상태는 관찰하지 않았다.
객체 색인
| 객체 | 저장소 | 목적 |
|---|---|---|
exchanges |
SQLite traffic index | 캡처한 HTTP exchange의 검색/목록 metadata를 저장한다. |
exchange_bodies |
SQLite traffic index | 작은 request/response body inline 값 또는 큰 body blob 참조를 저장한다. |
blob_refs |
SQLite traffic index | content-addressed body blob과 exchange 참조를 연결한다. |
ex_fts |
SQLite FTS5 virtual table | body text의 contentless trigram 검색 index다. 적용 실패 시 기능이 비활성화될 수 있다. |
exchanges
exchanges · 관계
이 대상을 사용하는 기능·모듈·계약 · 1개
- 계약 사용 · 트래픽에서 증거 snapshot까지 · Traffic.ExportHosts: read FROM; Traffic.ImportArchive: read/write FROM/INSERT; Traffic.readEvidence: read FROM; Traffic.Count: read FROM; Traffic.Get: read FROM/JOIN; Traffic.Hosts: read FROM; Traffic.Page: read FROM; Traffic.Search: read FROM; Traffic.deleteWhere: read/write DELETE/FROM; Traffic.hostTrees: read FROM; Traffic.hostsHaveNoExchanges: read FROM; Traffic.legacyBlobRefs: read FROM; Traffic.query: read FROM; Traffic.record: write INSERT · db-sqlite-access-exchanges-4f80e596da · db-sqlite-access-exchanges-b479f9f58f · db-sqlite-access-exchanges-9162ebd8fd · db-sqlite-access-exchanges-2bf40eda47 · db-sqlite-access-exchanges-cb9120717d · db-sqlite-access-exchanges-813a9379ef · db-sqlite-access-exchanges-aca94a3fc0 · db-sqlite-access-exchanges-a235da95f5 · db-sqlite-access-exchanges-4e84109cd8 · db-sqlite-access-exchanges-a26f83a3bb · db-sqlite-access-exchanges-267d0fd3c2 · db-sqlite-access-exchanges-1c22a6cb53 · db-sqlite-access-exchanges-c3c166aa3a · db-sqlite-access-exchanges-959f04152f
캡처한 HTTP exchange의 검색/목록 metadata를 저장한다.
정의 원본은 traffic/traffic.go다. 등록 근거
CREATE TABLE IF NOT EXISTS exchanges (
id TEXT PRIMARY KEY,
ts INTEGER,
host TEXT,
method TEXT,
url_template TEXT,
url TEXT,
status INTEGER,
content_type TEXT,
req_len INTEGER,
resp_len INTEGER,
path TEXT
);
인덱스
CREATE INDEX IF NOT EXISTS idx_ex_host ON exchanges(host);CREATE INDEX IF NOT EXISTS idx_ex_tmpl ON exchanges(host, url_template);CREATE INDEX IF NOT EXISTS idx_ex_ts ON exchanges(ts);CREATE INDEX IF NOT EXISTS idx_ex_status ON exchanges(status);CREATE INDEX IF NOT EXISTS idx_ex_resp ON exchanges(resp_len);
필드·접근 의미
| 필드 | null/default | 의미·encoding | 민감도 |
|---|---|---|---|
id TEXT |
schema상 null 허용 | ordinary rowid table의 non-INTEGER TEXT PRIMARY KEY라 implicit NOT NULL이 아니다; 앱 recorder는 항상 ID를 공급 |
일반 |
ts INTEGER |
null 허용 | time.Now().Unix()의 Unix seconds capture 시각 |
일반 |
host TEXT |
null 허용 | URL.Hostname() 결과라 port를 제외한 host |
민감 가능 |
method TEXT |
null 허용 | HTTP method | 일반 |
url_template TEXT |
null 허용 | query를 버린 URL.EscapedPath()에 TemplatePath를 적용해 numeric/UUID/긴 hex·token path segment를 치환한 template |
민감 가능 |
url TEXT |
null 허용 | 원 요청 URL | 민감/원문 |
status INTEGER |
null 허용 | 응답 status code | 일반 |
content_type TEXT |
null 허용 | 응답 Content-Type | 일반 |
req_len, resp_len INTEGER |
null 허용 | request/response byte 길이 | 일반 |
path TEXT |
null 허용 | 새 capture는 빈 값; non-empty 값은 legacy host/method tree의 상대 경로 | 민감 가능 |
Traffic.record가 같은 transaction에서 이 row와 body/blob reference/FTS row를 쓴다. public Traffic.Search는 host exact filter와 page만 제공한다. UI의 Page는 metadata·FTS filter를, agent tool이 호출하는 private query는 host/URL/body filter를 사용한다. Get, Hosts, Count와 deletion staging·GC도 읽고, deleteWhere가 관련 세 table과 FTS row를 함께 삭제한다. 다섯 index는 host/template/time/status/response-size filter와 정렬을 돕지만 capture마다 유지 비용을 낸다. FK가 없으므로 네 객체의 동일 ID 무결성은 이 transaction과 delete 순서가 유지한다. traffic store
ID는 <Unix-second>-<atomic sequence mod 10000>다. Sequence는 process 재시작 시 0에서 다시 시작하고 한 초에 10,000건을 넘으면 돌기 때문에 same-second restart·고속 capture에서 collision이 가능하다. INSERT OR REPLACE는 collision 시 기존 exchanges/exchange_bodies row를 대체하지만 구 blob_refs와 구 FTS row를 선행 정리하지 않는다. 충돌 빈도와 잔존 reference/index 영향은 실행 검증 전 U다.
exchange_bodies
exchange_bodies · 관계
이 대상을 사용하는 기능·모듈·계약 · 1개
- 계약 사용 · 트래픽에서 증거 snapshot까지 · Traffic.ExportHosts: read FROM; Traffic.ImportArchive: write INSERT; Traffic.readEvidence: read FROM; Traffic.Get: read FROM; Traffic.deleteWhere: write DELETE; Traffic.record: write INSERT · db-sqlite-access-exchange-bodies-7971225971 · db-sqlite-access-exchange-bodies-48e3443ec7 · db-sqlite-access-exchange-bodies-d512e84e76 · db-sqlite-access-exchange-bodies-ad6310aecb · db-sqlite-access-exchange-bodies-cd161de2ba · db-sqlite-access-exchange-bodies-51fc030662
작은 request/response body inline 값 또는 큰 body blob 참조를 저장한다.
정의 원본은 traffic/traffic.go다. 등록 근거
CREATE TABLE IF NOT EXISTS exchange_bodies (
id TEXT PRIMARY KEY,
req_head TEXT NOT NULL DEFAULT '',
req_body BLOB,
req_blob TEXT,
resp_head TEXT NOT NULL DEFAULT '',
resp_body BLOB,
resp_blob TEXT
);
필드·접근 의미
| 필드 | null/default | 의미·encoding | 민감도 |
|---|---|---|---|
id TEXT |
schema상 null 허용 | ordinary rowid table의 TEXT PRIMARY KEY; 앱 writer는 exchanges.id를 공급해 논리 연결하지만 FK는 없음 |
일반 |
req_head, resp_head TEXT |
null 불가, '' |
원 HTTP header text | 비밀/원문 |
req_body, resp_body BLOB |
null 허용 | 256 KiB 이하 body 전체 또는 큰 body의 8 KiB preview | 비밀/원문 |
req_blob, resp_blob TEXT |
null 허용 | 큰 body의 SHA-256 blob key | 민감 가능 |
Traffic.record가 INSERT OR REPLACE하고 Get이 exchanges와 join해 읽는다. blob file은 SQL transaction 전에 기록되므로 SQL rollback만으로 filesystem write가 되돌아가지 않는다. deleteWhere는 body row를 exchange보다 먼저 지운다. body/header redaction은 이 storage layer에 없으며 문서화한 크기 경계도 실행 환경의 row 내용을 검증한 결과는 아니다. traffic store
blob_refs
blob_refs · 관계
이 대상을 사용하는 기능·모듈·계약 · 1개
- 계약 사용 · 트래픽에서 증거 snapshot까지 · Traffic.ImportArchive: write INSERT; Traffic.deleteWhere: write DELETE; Traffic.gcBlobs: read FROM; Traffic.record: write INSERT · db-sqlite-access-blob-refs-12efa593f1 · db-sqlite-access-blob-refs-a443b6feb5 · db-sqlite-access-blob-refs-928f5df99f · db-sqlite-access-blob-refs-c0b4d474ee
content-addressed body blob과 exchange 참조를 연결한다.
정의 원본은 traffic/traffic.go다. 등록 근거
CREATE TABLE IF NOT EXISTS blob_refs (
hash TEXT NOT NULL,
exchange_id TEXT NOT NULL,
PRIMARY KEY (hash, exchange_id)
);
인덱스
CREATE INDEX IF NOT EXISTS idx_blob_refs_ex ON blob_refs(exchange_id);
필드·접근 의미
| 필드 | null/default | 의미·encoding | 민감도 |
|---|---|---|---|
hash TEXT |
composite PK, null 불가 | _blobs/sha256 content key |
민감 가능 |
exchange_id TEXT |
composite PK, null 불가 | 참조 exchange ID; FK는 없음 | 일반 |
Traffic.record가 body를 spill할 때 INSERT OR IGNORE하고, gcBlobs가 distinct hash를 읽어 filesystem sweep의 live set으로 쓴다. deleteWhere가 exchange별 reference를 지운다. idx_blob_refs_ex는 exchange 삭제 조회를 빠르게 하지만 hash별 GC는 composite PK의 leading hash를 사용한다. 자동 기간 보존은 없고 API의 host/all deletion과 task archive staging이 retention을 결정한다. traffic store
ex_fts virtual table
ex_fts virtual table · 관계
이 대상을 사용하는 기능·모듈·계약 · 1개
- 계약 사용 · 트래픽에서 증거 snapshot까지 · Traffic.ImportArchive: write INSERT; Traffic.compactIndex: write INSERT; Traffic.deleteWhere: write DELETE; Traffic.ftsFilter: read FROM; Traffic.reclaimChunk: write INSERT; Traffic.record: write INSERT · db-sqlite-access-ex-fts-e7acacd3ef · db-sqlite-access-ex-fts-446971b0d7 · db-sqlite-access-ex-fts-735362d242 · db-sqlite-access-ex-fts-973e53370c · db-sqlite-access-ex-fts-f9b44927da · db-sqlite-access-ex-fts-2b5c06a26b
request/response 본문 검색을 위한 contentless FTS5 index다. 원문은 크기에 따라 exchange_bodies inline body 또는 _blobs/sha256 CAS blob에 남고, FTS row는 rowid와 trigram index만 유지한다. 등록 근거
CREATE VIRTUAL TABLE IF NOT EXISTS ex_fts USING fts5(
content, tokenize='trigram', content='', contentless_delete=1
);
적용과 fallback
ftsSchema는 일반 index schema와 별도로 적용된다. build/runtime SQLite가 FTS5 또는 해당 option을 지원하지 않으면 traffic store는 초기화 자체를 실패시키지 않고 FTS 기능을 비활성화한다. 따라서 선언상 virtual table이 있어도 실제 DB에 존재한다고 단정할 수 없다. 일반 table 3개와 명시 index 6개는 별도 경로다.
content 한 필드는 URL, request/response header 전체, text로 판정된 request body와 response body의 각각 최대 4 MiB를 합쳐 만든다. 두 body만 합쳐도 잠재적으로 약 8 MiB이며 URL/header는 별도다. contentless이므로 원문을 보관하지 않고 rowid로 exchanges에 논리 연결된다. UI Page의 broad/body filter와 agent tool private query의 body_contains가 3자 이상 검색에 이 index를 쓴다. public Traffic.Search는 FTS를 사용하지 않는 host-only 목록 API다. deleteWhere와 background merge/reclaim은 tombstone을 정리한다. 원문에 credential·cookie·token이 있으면 trigram에도 파생 정보가 남을 수 있다. FTS availability, 실제 index 크기와 merge 비용은 운영 파일 관찰 전에는 U다.
Archive export는 spilled body의 CAS blob 전체를 함께 복사한다. traffic.json의 inline body는 256 KiB 이하 body이면 전체이고, spill된 body이면 8 KiB preview(이진 body는 짧은 표시 문자열)다. Import는 blob을 복원한 뒤에도 FTS를 이 URL/header/inline 값으로만 재생성하므로, 원래 capture에서 8 KiB를 넘어 4 MiB까지 색인된 spilled text는 restore 후 8 KiB 밖이 재색인되지 않는다. body bytes 복원과 FTS 검색 커버리지는 다른 일관성 경계다.
SQLite 의미 판정
| 축 | 결과 | 미확인 |
|---|---|---|
| 일반 table/field | 3 table / 20 field (C) | runtime file drift |
| FTS virtual table/field | 1 / 1 선언(C), 실제 가용성 A | build의 FTS5 option과 ftsSchema 적용 결과 |
| 명시 index | 6/6 (C) | 실제 query plan·cardinality |
| writer/reader와 transaction 경계 | Traffic.record, query/get/delete/GC 경로 정적 확인(C/A) |
crash injection과 동시 부하 |
| retention/role | API deletion·archive staging만 확인(A) | 운영 보존 기간, filesystem ACL·backup |
Filesystem 경계
Traffic.Open은 root 아래 _index/index.sqlite, _blobs/sha256/..., _ca를 사용한다. 현재 Traffic.record는 새 capture를 SQLite에 기록하고, 256 KiB를 넘는 body만 SHA-256 content-addressed blob으로 spill하며 8 KiB preview를 SQLite에 남긴다. 새 row의 exchanges.path는 빈 문자열이고 per-request .http/host-method tree를 만들지 않는다. non-empty path와 host/method tree는 이전 저장 형식의 조회·삭제·archive/recovery 호환 경계다. traffic store
SQLite transaction은 index/body/blob reference를 묶지만 blob file write는 transaction을 열기 전에 일어난다. spill과 archive import는 final CAS path에 직접 write하고 temp+fsync+rename이나 destination hash 재검증을 하지 않는다. Crash/I/O로 partial file이 남으면 현재 capture는 빈 hash+8 KiB clip으로 commit할 수 있고, 뒤의 동일 body는 os.Stat만으로 그 path를 신뢰해 hash reference를 저장할 수 있다. 성공한 spill 뒤 SQL이 실패하면 orphan CAS file도 남는다. Blob directory 생성·file write 실패로 SQLite commit만 계속되면 원문이 비가역적으로 잘릴 수 있다. index.sqlite만 백업하면 큰 body가 복원되지 않는다. 현재 capture에 legacy host tree는 필요 없지만 non-empty exchanges.path가 남은 설치에서는 그 tree도 함께 보존해야 한다. HTTPS MITM이 proxy/protocol 원인으로 실패한 host는 이후 transparent tunnel로 fail-open하며 그 트래픽은 기록되지 않는다.
초기화·commit·recovery matrix
| 경로 | 순서·보장 | 실패/운영 경계 |
|---|---|---|
| SQLite open | DSN과 pinned init connection에 WAL, 5 s busy timeout 설정 | index path에 ?가 있으면 bare DSN fallback이므로 busy timeout은 init connection에만 적용되고 pool의 후속 connection에는 보장되지 않음 |
| Auto-vacuum | sqlite_master table 0개인 최초 DB에서만 auto_vacuum=incremental+VACUUM |
기존 mode 0 DB는 ordinary delete로 file이 줄지 않을 수 있고, DeleteAll 후 full compaction이 conversion path |
| Capture | body가 256 KiB를 넘으면 final CAS path에 직접 write → SQLite metadata/body/ref/FTS transaction | temp+fsync+rename/hash 재검증이 없어 partial destination을 다음 동일-body capture가 os.Stat만으로 신뢰할 수 있음; CAS 성공 뒤 SQLite 실패는 orphan, write 실패 뒤 DB 성공은 빈 hash+8 KiB clip과 비가역 truncation을 남김 |
| Archive export/import | small body 전체 또는 spilled body 8 KiB preview+CAS blob을 package화; import는 snapshot.Blobs 각 entry의 형식과 incoming bytes SHA-256을 검증/copy한 뒤 SQLite transaction |
destination CAS path가 이미 있으면 기존 file은 overwrite·재검증하지 않음. traffic.json의 ReqBlob/RespBlob은 snapshot.Blobs membership·hash format·destination 존재를 교차 검증하지 않아 내부 불일치 archive가 missing/invalid reference를 commit할 수 있고, outer package checksum도 이 cross-reference invariant를 보장하지 않음. DB 실패 또는 duplicate-only restore는 선행 copied blob을 orphan으로 남길 수 있으며 FTS는 inline 값으로만 재생성 |
| Host/task delete | legacy tree를 same-filesystem rename으로 stage → SQLite delete; commit 실패면 tree restore, commit 후 unlink/GC/reclaim | unlink/GC/reclaim은 best-effort라 committed row delete 후 file garbage가 남을 수 있음 |
| Task archive | PostgreSQL archive/compaction commit → staged SQLite delete commit | 두 commit 사이 crash gap은 persistent journal과 startup RecoverHostDeleteStages가 PG state를 조회해 complete/restore; atomic cross-store transaction은 아님 |
| Requested modes | traffic root/index/CAS/CA directory 0755, CAS blob 0644; archive blob dir/staging 0700, archive/journal file 0600 |
umask, inherited ACL, container volume/mount option이 effective permission을 바꿀 수 있음 |