Skip to content

Commit 5eb97f3

Browse files
author
AnkleBreaker Cowork
committed
v0.1.0 - standalone C/C++ SDK: C99 ABI, libcurl transport, offline sidecars, session log + unclean-shutdown recovery, CI matrix
0 parents  commit 5eb97f3

56 files changed

Lines changed: 5007 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
build-and-test:
10+
name: ${{ matrix.os }}
11+
runs-on: ${{ matrix.os }}
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
os: [windows-latest, ubuntu-latest, macos-latest]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Install libcurl (Linux)
21+
if: runner.os == 'Linux'
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y libcurl4-openssl-dev
25+
26+
- name: Configure
27+
run: >
28+
cmake -B build
29+
-DCMAKE_BUILD_TYPE=Release
30+
-DTOMBSTONE_BUILD_EXAMPLES=ON
31+
-DTOMBSTONE_BUILD_TESTS=ON
32+
-DTOMBSTONE_BUILD_STATIC=ON
33+
34+
- name: Build (shared lib + static + examples + tests)
35+
run: cmake --build build --config Release --parallel
36+
37+
- name: Test
38+
run: ctest --test-dir build -C Release --output-on-failure
39+
40+
- name: Run sidecar demo (offline path)
41+
if: runner.os != 'Windows'
42+
run: ./build/tombstone_crash_sidecar_demo
43+
44+
- name: Run sidecar demo (offline path, Windows)
45+
if: runner.os == 'Windows'
46+
run: .\build\Release\tombstone_crash_sidecar_demo.exe

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Build trees
2+
build/
3+
out/
4+
cmake-build-*/
5+
6+
# IDE
7+
.vs/
8+
.vscode/
9+
.idea/
10+
*.user
11+
12+
# Example scratch data
13+
tombstone-demo-data/

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Changelog
2+
3+
All notable changes to the Tombstone Native SDK.
4+
5+
## [0.1.0] - 2026-06-10
6+
7+
First public cut: capture-local telemetry client with full wire-protocol
8+
parity with the Tombstone Unity SDK (no OS-level crash capture yet — that is
9+
Phase 2 via a sentry-native/Crashpad fork).
10+
11+
### Added
12+
13+
- Pure C99 public API (`include/tombstone/tombstone.h`): init/shutdown,
14+
set_user, set_consent, add_breadcrumb, track_event, report_crash,
15+
report_bug, log_line, flush — every call returns `tombstone_result`,
16+
nothing throws across the ABI.
17+
- Dependency-free JSON writer mirroring the server's Zod schemas byte-for-byte
18+
(optionals omitted, explicit `log` boolean, client-side length clamps,
19+
UTF-8-safe truncation).
20+
- libcurl HTTPS transport: bearer-authenticated ingest POSTs + presigned S3
21+
log PUTs (no auth header on PUTs).
22+
- Background worker thread: bounded drop-oldest queue, exponential backoff
23+
(2s→32s, 5 attempts), poison-4xx drop / 429+5xx retry classification.
24+
- Offline sidecar queue (`data_dir/pending/{crashes,bug-reports,events}/*.json`,
25+
raw ingest bodies, uploader-compatible), drained on next init; write-ahead
26+
durability for crashes and bug reports.
27+
- Breadcrumb ring (64 slots, preallocated, thread-safe).
28+
- Per-signature crash dedupe (≤1 report/min, suppressed repeats become a
29+
counter breadcrumb).
30+
- Rolling session log (512 KB cap, front-trim, `session.log`
31+
`previous-session.log` rotation) with presigned upload on crash/bug reports.
32+
- Unclean-shutdown detection (`session.lock` marker) with a synthetic
33+
`unclean-shutdown` crash report carrying the previous session's log.
34+
- Session heartbeat timer thread (15–600 s interval, ephemeral delivery).
35+
- Consent gate (GDPR/store policy): nothing recorded or sent while off.
36+
- CMake build (shared + optional static, install rules, warnings-as-errors),
37+
C examples, dependency-free unit tests wired into CTest, GitHub Actions CI
38+
matrix (Windows MSVC / Linux GCC / macOS Clang).

CMakeLists.txt

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(tombstone VERSION 0.1.0 LANGUAGES C CXX)
3+
4+
option(TOMBSTONE_BUILD_STATIC "Also build a static tombstone library" OFF)
5+
option(TOMBSTONE_BUILD_EXAMPLES "Build the example programs" ON)
6+
option(TOMBSTONE_BUILD_TESTS "Build and register the unit tests" ON)
7+
8+
set(CMAKE_CXX_STANDARD 17)
9+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
10+
set(CMAKE_CXX_EXTENSIONS OFF)
11+
set(CMAKE_C_STANDARD 99)
12+
set(CMAKE_C_STANDARD_REQUIRED ON)
13+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
14+
15+
find_package(Threads REQUIRED)
16+
17+
# ---------------------------------------------------------------------------
18+
# Warnings-as-errors for OUR targets only (never for fetched dependencies).
19+
# ---------------------------------------------------------------------------
20+
add_library(tombstone_warnings INTERFACE)
21+
if(MSVC)
22+
target_compile_options(tombstone_warnings INTERFACE /W4 /WX /permissive- /utf-8)
23+
target_compile_definitions(tombstone_warnings INTERFACE _CRT_SECURE_NO_WARNINGS NOMINMAX)
24+
else()
25+
target_compile_options(tombstone_warnings INTERFACE -Wall -Wextra -Werror)
26+
endif()
27+
28+
# ---------------------------------------------------------------------------
29+
# libcurl: system package first, FetchContent fallback (HTTP+TLS only).
30+
# ---------------------------------------------------------------------------
31+
find_package(CURL QUIET)
32+
if(NOT CURL_FOUND)
33+
message(STATUS "tombstone: system libcurl not found; fetching curl 8.11.1")
34+
include(FetchContent)
35+
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
36+
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
37+
set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE)
38+
set(BUILD_LIBCURL_DOCS OFF CACHE BOOL "" FORCE)
39+
set(BUILD_MISC_DOCS OFF CACHE BOOL "" FORCE)
40+
set(ENABLE_CURL_MANUAL OFF CACHE BOOL "" FORCE)
41+
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
42+
set(HTTP_ONLY ON CACHE BOOL "" FORCE)
43+
set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE)
44+
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE)
45+
set(CURL_BROTLI OFF CACHE BOOL "" FORCE)
46+
set(CURL_ZSTD OFF CACHE BOOL "" FORCE)
47+
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
48+
set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
49+
if(WIN32)
50+
set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE)
51+
elseif(APPLE)
52+
set(CURL_USE_SECTRANSP ON CACHE BOOL "" FORCE)
53+
else()
54+
set(CURL_USE_OPENSSL ON CACHE BOOL "" FORCE)
55+
endif()
56+
FetchContent_Declare(
57+
curl
58+
URL https://github.com/curl/curl/releases/download/curl-8_11_1/curl-8.11.1.tar.gz
59+
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
60+
)
61+
FetchContent_MakeAvailable(curl)
62+
endif()
63+
64+
# ---------------------------------------------------------------------------
65+
# Core object library: the PURE parts (no curl, no threads-of-its-own).
66+
# Shared by the SDK and the unit tests, so tests never need a TLS stack.
67+
# ---------------------------------------------------------------------------
68+
set(TOMBSTONE_CORE_SOURCES
69+
src/breadcrumb_ring.cpp
70+
src/clock.cpp
71+
src/dedupe.cpp
72+
src/json_scan.cpp
73+
src/json_writer.cpp
74+
src/payloads.cpp
75+
src/platform.cpp
76+
src/sdk_log.cpp
77+
src/session_log.cpp
78+
src/session_marker.cpp
79+
src/sidecar_queue.cpp
80+
src/signature.cpp
81+
)
82+
83+
add_library(tombstone_core OBJECT ${TOMBSTONE_CORE_SOURCES})
84+
target_include_directories(tombstone_core
85+
PUBLIC
86+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
87+
PRIVATE
88+
${CMAKE_CURRENT_SOURCE_DIR}/src
89+
)
90+
target_link_libraries(tombstone_core PRIVATE tombstone_warnings)
91+
92+
set(TOMBSTONE_IMPURE_SOURCES
93+
src/client.cpp
94+
src/client_reports.cpp
95+
src/tombstone_api.cpp
96+
src/transport_curl.cpp
97+
src/worker.cpp
98+
)
99+
100+
# ---------------------------------------------------------------------------
101+
# The shared library (the artifact engines load).
102+
# ---------------------------------------------------------------------------
103+
add_library(tombstone SHARED ${TOMBSTONE_IMPURE_SOURCES} $<TARGET_OBJECTS:tombstone_core>)
104+
target_include_directories(tombstone
105+
PUBLIC
106+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
107+
$<INSTALL_INTERFACE:include>
108+
PRIVATE
109+
${CMAKE_CURRENT_SOURCE_DIR}/src
110+
)
111+
target_compile_definitions(tombstone PRIVATE TOMBSTONE_EXPORTS)
112+
target_link_libraries(tombstone PRIVATE CURL::libcurl Threads::Threads tombstone_warnings)
113+
set_target_properties(tombstone PROPERTIES
114+
VERSION ${PROJECT_VERSION}
115+
SOVERSION ${PROJECT_VERSION_MAJOR}
116+
CXX_VISIBILITY_PRESET hidden
117+
VISIBILITY_INLINES_HIDDEN ON
118+
)
119+
120+
# Optional static variant. Consumers must define TOMBSTONE_STATIC, which the
121+
# INTERFACE definition propagates automatically.
122+
if(TOMBSTONE_BUILD_STATIC)
123+
add_library(tombstone_static STATIC ${TOMBSTONE_IMPURE_SOURCES} $<TARGET_OBJECTS:tombstone_core>)
124+
target_include_directories(tombstone_static
125+
PUBLIC
126+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
127+
$<INSTALL_INTERFACE:include>
128+
PRIVATE
129+
${CMAKE_CURRENT_SOURCE_DIR}/src
130+
)
131+
target_compile_definitions(tombstone_static PUBLIC TOMBSTONE_STATIC)
132+
target_link_libraries(tombstone_static PRIVATE CURL::libcurl Threads::Threads tombstone_warnings)
133+
endif()
134+
135+
# ---------------------------------------------------------------------------
136+
# Examples (pure C99 consumers of the shared library).
137+
# ---------------------------------------------------------------------------
138+
if(TOMBSTONE_BUILD_EXAMPLES)
139+
add_executable(tombstone_minimal examples/minimal.c)
140+
target_link_libraries(tombstone_minimal PRIVATE tombstone)
141+
142+
add_executable(tombstone_crash_sidecar_demo examples/crash_sidecar_demo.c)
143+
target_link_libraries(tombstone_crash_sidecar_demo PRIVATE tombstone)
144+
endif()
145+
146+
# ---------------------------------------------------------------------------
147+
# Tests: dependency-free runner over the pure core; one CTest entry per suite.
148+
# ---------------------------------------------------------------------------
149+
if(TOMBSTONE_BUILD_TESTS)
150+
enable_testing()
151+
add_executable(tombstone_tests
152+
tests/main.cpp
153+
tests/test_breadcrumb_ring.cpp
154+
tests/test_clock.cpp
155+
tests/test_dedupe.cpp
156+
tests/test_json_scan.cpp
157+
tests/test_json_writer.cpp
158+
tests/test_payloads.cpp
159+
tests/test_session_log.cpp
160+
tests/test_session_marker.cpp
161+
tests/test_sidecar.cpp
162+
tests/test_signature.cpp
163+
$<TARGET_OBJECTS:tombstone_core>
164+
)
165+
target_include_directories(tombstone_tests PRIVATE
166+
${CMAKE_CURRENT_SOURCE_DIR}/src
167+
${CMAKE_CURRENT_SOURCE_DIR}/include
168+
${CMAKE_CURRENT_SOURCE_DIR}/tests
169+
)
170+
target_link_libraries(tombstone_tests PRIVATE Threads::Threads tombstone_warnings)
171+
172+
foreach(suite
173+
breadcrumb_ring clock dedupe json_scan json_writer
174+
payloads session_log session_marker sidecar signature)
175+
add_test(NAME ${suite} COMMAND tombstone_tests ${suite})
176+
endforeach()
177+
endif()
178+
179+
# ---------------------------------------------------------------------------
180+
# Install rules.
181+
# ---------------------------------------------------------------------------
182+
include(GNUInstallDirs)
183+
install(TARGETS tombstone
184+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
185+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
186+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
187+
)
188+
if(TOMBSTONE_BUILD_STATIC)
189+
install(TARGETS tombstone_static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
190+
endif()
191+
install(DIRECTORY include/tombstone DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 AnkleBreaker Studio
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)