aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 07:06:27 +0100
committereunhhu <[email protected]>2026-09-29 07:06:27 +0100
commita052b95c8135cdc5f9b91f8efc991cac80fbbe8b (patch)
tree2b7b0d7a0ccd5bc6ceaa603a01eb87deb6c1f2e8
parent0b4a9f7225b5873e606106b8eaf9174b254c0c0f (diff)
downloadpure-a052b95c8135cdc5f9b91f8efc991cac80fbbe8b.tar.gz
pure-a052b95c8135cdc5f9b91f8efc991cac80fbbe8b.zip
Put the links and the crypto they use in link/, a part of their own
libpure-link.a holds them; pure-serve and the M5 harness link it instead of naming its source files.
-rw-r--r--Makefile32
-rw-r--r--README.md3
-rw-r--r--docs/research/093-connectionless-link.md2
-rw-r--r--docs/research/096-per-delivery-cpu.md2
-rw-r--r--link/crypto-openssl.c (renamed from serve/crypto-openssl.c)2
-rw-r--r--link/crypto.c (renamed from serve/crypto.c)4
-rw-r--r--link/crypto.h (renamed from serve/crypto.h)8
-rw-r--r--link/link.c (renamed from serve/link.c)2
-rw-r--r--link/link.h (renamed from serve/link.h)0
-rw-r--r--link/seal.h (renamed from serve/seal.h)4
-rw-r--r--link/tls.h (renamed from serve/tls.h)2
-rw-r--r--research/experiments/019-idle-links/links.c4
-rw-r--r--research/experiments/019-idle-links/run.py11
-rw-r--r--serve/deliver.c2
-rw-r--r--serve/deliver.h2
-rw-r--r--serve/serve.c2
16 files changed, 47 insertions, 35 deletions
diff --git a/Makefile b/Makefile
index aeb0b09..427a08a 100644
--- a/Makefile
+++ b/Makefile
@@ -10,24 +10,29 @@ BUILD ?= build
# states as drafts; state.h and store.h are what it asks of storage.
# store/ the tree store (one file; commits are one contiguous write and one
# sync; formats PURETRE3 and PUREREC1).
-# serve/ pure-serve: the HTTP host, outbound delivery, JSON in place; with
-# pure-crypto.so beside it, OpenSSL's part (TLS to https receivers,
-# sealed sends), mapped only by a server that uses one.
+# link/ links without connections (UDP, sealed, no handshake) and the
+# crypto they and sealed sends use; with pure-crypto.so beside it,
+# OpenSSL's part (TLS to https receivers, sealing), mapped only by a
+# process that uses one.
+# serve/ pure-serve: the HTTP host, outbound delivery, JSON in place.
# cli/ command-line hosts.
# libpure.a is the app model with the tree store: what a host embeds (Vega's
-# surfaces link it). prototypes/ keeps the earlier document runtime (000-077).
+# surfaces link it); libpure-link.a is the links with the crypto loader.
+# prototypes/ keeps the earlier document runtime (000-077).
# Unit tests sit by their part (core/test-module.c, store/test-tree-store.c,
# store/test-log.c, serve/test-message-json.c);
# tests/check.py checks the hosts end to end.
INCLUDES = -Icore
MODEL = core/module.c core/module-list.c core/module-save.c core/module-text.c core/module-store.c core/flow.c
STORE = store/store-tree.c store/store-log.c
-SERVE = serve/serve.c serve/deliver.c serve/link.c serve/message-json.c serve/crypto.c
-HEADERS = $(wildcard core/*.h) $(wildcard serve/*.h) $(wildcard store/*.h)
+LINK = link/link.c link/crypto.c
+SERVE = serve/serve.c serve/deliver.c serve/message-json.c
+HEADERS = $(wildcard core/*.h) $(wildcard link/*.h) $(wildcard serve/*.h) $(wildcard store/*.h)
OBJECTS = $(patsubst %.c,$(BUILD)/obj/%.o,$(MODEL) $(STORE))
+LINK_OBJECTS = $(patsubst %.c,$(BUILD)/obj/%.o,$(LINK))
.PHONY: all check
-all: $(BUILD)/libpure.a $(BUILD)/pure-serve $(BUILD)/pure-crypto.so $(BUILD)/pure-run $(BUILD)/pure-edit-module
+all: $(BUILD)/libpure.a $(BUILD)/libpure-link.a $(BUILD)/pure-serve $(BUILD)/pure-crypto.so $(BUILD)/pure-run $(BUILD)/pure-edit-module
$(BUILD)/obj/%.o: %.c $(HEADERS)
@mkdir -p $(dir $@)
@@ -37,15 +42,20 @@ $(BUILD)/libpure.a: $(OBJECTS)
rm -f $@
$(AR) rcs $@ $^
+$(BUILD)/libpure-link.a: $(LINK_OBJECTS)
+ rm -f $@
+ $(AR) rcs $@ $^
+
# The runtime's tickets and budgets, with effects completed off the owner thread.
# It links after the library, last as it always was, so code keeps its place.
# The app model's functions are exported for a surface it maps (serve/surface.h).
-$(BUILD)/pure-serve: $(SERVE) core/runtime.c $(BUILD)/libpure.a $(HEADERS)
- $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -Iserve -DPURE_ASYNC_EFFECTS $(SERVE) $(BUILD)/libpure.a core/runtime.c \
+$(BUILD)/pure-serve: $(SERVE) core/runtime.c $(BUILD)/libpure.a $(BUILD)/libpure-link.a $(HEADERS)
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -Ilink -Iserve -DPURE_ASYNC_EFFECTS $(SERVE) $(BUILD)/libpure-link.a \
+ $(BUILD)/libpure.a core/runtime.c \
$(LDFLAGS) -Wl,--export-dynamic-symbol='pure_*' -lm -pthread $(LDLIBS) -o $@
-$(BUILD)/pure-crypto.so: serve/crypto-openssl.c $(HEADERS)
- $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -Iserve -fPIC -shared serve/crypto-openssl.c $(LDFLAGS) -lssl -lcrypto \
+$(BUILD)/pure-crypto.so: link/crypto-openssl.c $(HEADERS)
+ $(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -shared link/crypto-openssl.c $(LDFLAGS) -lssl -lcrypto \
$(LDLIBS) -o $@
$(BUILD)/pure-run: cli/pure-run.c $(BUILD)/libpure.a $(HEADERS)
diff --git a/README.md b/README.md
index 54b7987..93ccda2 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,8 @@ The source follows the principle below, a small core with the rest selected arou
|---|---|---|
| `core/` | execution, ownership, limits and completion (`runtime.c`); the app model: modules verified before they run, events as transactions, states as drafts (`module*.c`); what the core asks of storage (`state.h`, `store.h`) | `build/libpure.a` (with the store) |
| `store/` | the tree store: one file, a commit is one contiguous write and one sync (formats `PURETRE3`, `PUREREC1`) | in `libpure.a` |
-| `serve/` | `pure-serve`: the HTTP host, outbound delivery, JSON read in place, surfaces for people run in the node ([010](docs/design/010-surfaces.md)); OpenSSL's part (TLS to https receivers, sealed sends) apart, mapped only by a server that uses one | `build/pure-serve`, `build/pure-crypto.so` |
+| `link/` | links without connections: saved sends over UDP, sealed with the two nodes' keys, no handshake ([011](docs/design/011-pure-transport.md)); the crypto they and sealed sends use, OpenSSL's part apart, mapped only by a process that uses one | `build/libpure-link.a`, `build/pure-crypto.so` |
+| `serve/` | `pure-serve`: the HTTP host, outbound delivery, JSON read in place, surfaces for people run in the node ([010](docs/design/010-surfaces.md)) | `build/pure-serve` |
| `cli/` | command-line hosts | `build/pure-run`, `build/pure-edit-module` |
| `prototypes/` | the earlier document runtime (000-077), built against `core/` and `store/` | `make -C prototypes/connections` |
| `tests/` | the hosts checked end to end, with apps built by aup (the sibling checkout) | `make check` |
diff --git a/docs/research/093-connectionless-link.md b/docs/research/093-connectionless-link.md
index ad36e47..da2a979 100644
--- a/docs/research/093-connectionless-link.md
+++ b/docs/research/093-connectionless-link.md
@@ -34,7 +34,7 @@
- 새로 저장하는 전송 상태는 없다. 011이 적은 "링크의 행(약 100 B)"도 필요 없었다. 첫 에폭에서 링크가 저장소에 더하는 것은
0 바이트다.
-**보내는 쪽([serve/link.c](../../serve/link.c)의 `PureLink`).**
+**보내는 쪽([link/link.c](../../link/link.c)의 `PureLink`).**
- 전달선과 같은 자리에 붙는다. `--send OUTPUT=pure://host:port/INPUT`로 열고, 받는 노드의 키는 `--seal`로 준다.
- outbox의 기록을 패킷에 채워 보낸다. 한 패킷에 여러 기록이 들고, 큰 기록은 1000 B 조각으로 나눈다.
diff --git a/docs/research/096-per-delivery-cpu.md b/docs/research/096-per-delivery-cpu.md
index e038a7c..df2718c 100644
--- a/docs/research/096-per-delivery-cpu.md
+++ b/docs/research/096-per-delivery-cpu.md
@@ -38,7 +38,7 @@ pure-serve 두 대를 잇고, 보내는 앱에 주문 2만 건을 쌓은 뒤 받
| 커밋 | 무엇 | 기록당 CPU |
|---|---|---:|
| 처음(pure-serve, 2만 건) | | 3.5 µs |
-| [0f365f0](../../serve/link.c) | 쌓인 것을 훑지 않는다. GSO 묶음과 recvmmsg | 1.0 µs(pure-serve) |
+| [0f365f0](../../link/link.c) | 쌓인 것을 훑지 않는다. GSO 묶음과 recvmmsg | 1.0 µs(pure-serve) |
| e19aa30 | 링크 스레드를 잠들었을 때만 한 번 깨운다 | 0.49 µs |
| 54035fe | 작은 기록을 RECORDS 프레임에 담는다(머리 19 B → 1–2 B) | 0.447 µs |
| 803626a | 받을 때 UDP GRO, 리스너의 AES 키는 run이 바뀔 때만 넣는다 | 0.394 µs |
diff --git a/serve/crypto-openssl.c b/link/crypto-openssl.c
index 67ce5fd..6bdf6fb 100644
--- a/serve/crypto-openssl.c
+++ b/link/crypto-openssl.c
@@ -1,5 +1,5 @@
#define _GNU_SOURCE
-/* pure-crypto.so: what pure-serve asks of OpenSSL (serve/crypto.h), mapped
+/* pure-crypto.so: what pure-serve asks of OpenSSL (link/crypto.h), mapped
* only when a server seals, opens a seal or reaches an https receiver. It
* exports one table, pure_crypto. */
#include "crypto.h"
diff --git a/serve/crypto.c b/link/crypto.c
index da63b37..2fa7d44 100644
--- a/serve/crypto.c
+++ b/link/crypto.c
@@ -1,6 +1,6 @@
#define _GNU_SOURCE
-/* Sealing and TLS for pure-serve (serve/seal.h, serve/tls.h), by way of
- * pure-crypto.so (serve/crypto.h): mapped, and OpenSSL with it, the first
+/* Sealing and TLS for pure-serve (link/seal.h, link/tls.h), by way of
+ * pure-crypto.so (link/crypto.h): mapped, and OpenSSL with it, the first
* time a server needs it. What needs no OpenSSL (a key's hex, the suite
* this machine seals with) is here. */
#include "crypto.h"
diff --git a/serve/crypto.h b/link/crypto.h
index 53e9c2c..53ac785 100644
--- a/serve/crypto.h
+++ b/link/crypto.h
@@ -4,9 +4,9 @@
#include "tls.h"
/* What pure-serve asks of OpenSSL, as the table pure-crypto.so exports
- * (serve/crypto-openssl.c). pure-serve maps it, and OpenSSL with it, the
+ * (link/crypto-openssl.c). pure-serve maps it, and OpenSSL with it, the
* first time a server seals, opens a seal or connects to an https receiver
- * (serve/crypto.c): one that does none of these maps no OpenSSL, 2.3 MiB
+ * (link/crypto.c): one that does none of these maps no OpenSSL, 2.3 MiB
* less resident and 0.6 MiB less of its own. */
enum { PURE_CRYPTO_VERSION = 4 };
@@ -20,7 +20,7 @@ typedef struct {
int (*tls_read)(PureTls *, void *bytes, int size);
int (*tls_write)(PureTls *, const void *bytes, int size);
void (*tls_close)(PureTls *);
- /* Sealing, as serve/seal.h says; a sender's sessions in the suite named. */
+ /* Sealing, as link/seal.h says; a sender's sessions in the suite named. */
PureSealKey *(*seal_key)(const char *path, char public_hex[2 * PURE_SEAL_KEY + 1], char *error, size_t size);
void (*seal_key_free)(PureSealKey *);
PureSealer *(*sealer)(const char *suite, const unsigned char peer[PURE_SEAL_KEY], const PureSealKey *own,
@@ -33,7 +33,7 @@ typedef struct {
bool (*unseal)(PureOpener *, uint64_t n, const char *aad, const unsigned char *sealed, size_t size,
unsigned char *plain);
void (*opener_free)(PureOpener *);
- /* Links, as serve/seal.h says. */
+ /* Links, as link/seal.h says. */
bool (*link_secret)(const PureSealKey *, const unsigned char peer[PURE_SEAL_KEY], unsigned char out[32]);
bool (*link_derive)(const unsigned char secret[32], const unsigned char *info, size_t info_size, unsigned char *out,
size_t out_size);
diff --git a/serve/link.c b/link/link.c
index e2da052..9c6794b 100644
--- a/serve/link.c
+++ b/link/link.c
@@ -1,5 +1,5 @@
#define _GNU_SOURCE
-/* Links without connections (serve/link.h, docs/design/011-pure-transport.md). */
+/* Links without connections (link/link.h, docs/design/011-pure-transport.md). */
#include "link.h"
#include <arpa/inet.h>
#include <errno.h>
diff --git a/serve/link.h b/link/link.h
index 83cc645..83cc645 100644
--- a/serve/link.h
+++ b/link/link.h
diff --git a/serve/seal.h b/link/seal.h
index 18e3e5f..9245f25 100644
--- a/serve/seal.h
+++ b/link/seal.h
@@ -13,7 +13,7 @@
* (one encapsulation) and numbers its seals in it; a receiver opens a
* session once and then each seal by its number. A private key lives in
* OpenSSL's secure heap: locked in memory, out of core dumps, cleared when
- * freed. OpenSSL comes with pure-crypto.so (serve/crypto.c), mapped the
+ * freed. OpenSSL comes with pure-crypto.so (link/crypto.c), mapped the
* first time a key is read or a session made. */
enum { PURE_SEAL_KEY = 32, PURE_SEAL_OVERHEAD = 16 }; /* a public key; a seal's tag */
@@ -54,7 +54,7 @@ bool pure_unseal(PureOpener *, uint64_t n, const char *aad, const unsigned char
unsigned char *plain);
void pure_opener_free(PureOpener *);
-/* Links (serve/link.h): the secret two nodes share (X25519 of this node's
+/* Links (link/link.h): the secret two nodes share (X25519 of this node's
* private key and the other's public key: the same on both sides, and
* never all zeros), keys from it (HKDF-SHA256 with the info given), and
* AES-128-GCM under such a key with the nonce the caller gives (sealing may
diff --git a/serve/tls.h b/link/tls.h
index 704924d..3939f21 100644
--- a/serve/tls.h
+++ b/link/tls.h
@@ -3,7 +3,7 @@
#include <stdbool.h>
#include <stddef.h>
-/* TLS to https receivers (serve/crypto.c): one client context, verifying a
+/* TLS to https receivers (link/crypto.c): one client context, verifying a
* receiver's certificate against the system's CAs or ca_file, and its name. */
typedef struct PureTls PureTls;
/* The context, made once; false with why in error when it cannot be. */
diff --git a/research/experiments/019-idle-links/links.c b/research/experiments/019-idle-links/links.c
index 732429a..666ac29 100644
--- a/research/experiments/019-idle-links/links.c
+++ b/research/experiments/019-idle-links/links.c
@@ -1,7 +1,7 @@
#define _GNU_SOURCE
/* Many senders to one Pure node over links without connections
* (docs/design/011-pure-transport.md, M1). Written from the packet format in
- * serve/link.h alone, with OpenSSL: a second implementation of it.
+ * link/link.h alone, with OpenSSL: a second implementation of it.
*
* links keys N FILE
* N X25519 keys into FILE (each a private then a public half, 32 bytes
@@ -111,7 +111,7 @@ static int keys(size_t n, const char *path) {
return 0;
}
-/* ---- Keys of a link (serve/link.h): X25519, HKDF-SHA256, AES-128-GCM */
+/* ---- Keys of a link (link/link.h): X25519, HKDF-SHA256, AES-128-GCM */
static bool secret_of(const unsigned char own[2 * KEY], const unsigned char peer[KEY], unsigned char out[32]) {
EVP_PKEY *mine = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, own, KEY);
diff --git a/research/experiments/019-idle-links/run.py b/research/experiments/019-idle-links/run.py
index 96a0cbc..d10cb3f 100644
--- a/research/experiments/019-idle-links/run.py
+++ b/research/experiments/019-idle-links/run.py
@@ -552,11 +552,12 @@ def main():
args = parser.parse_args()
counts = [int(c) for c in args.counts.split(',')]
if args.transport:
- subprocess.run(['make', '-s', '-C', str(ROOT), 'build/libpure.a', 'build/pure-crypto.so'], check=True)
- subprocess.run(['cc', '-O2', '-g', '-std=c17', '-Wall', '-Wextra', '-Werror', f'-I{ROOT}/core', f'-I{ROOT}/serve',
- '-DPURE_ASYNC_EFFECTS', str(Path(__file__).parent / 'transport.c'), str(ROOT / 'serve/link.c'),
- str(ROOT / 'serve/crypto.c'), str(ROOT / 'build/libpure.a'), '-lm', '-pthread', '-ldl', '-o',
- str(ROOT / 'build/link-transport')], check=True)
+ subprocess.run(['make', '-s', '-C', str(ROOT), 'build/libpure.a', 'build/libpure-link.a', 'build/pure-crypto.so'],
+ check=True)
+ subprocess.run(['cc', '-O2', '-g', '-std=c17', '-Wall', '-Wextra', '-Werror', f'-I{ROOT}/core', f'-I{ROOT}/link',
+ str(Path(__file__).parent / 'transport.c'), str(ROOT / 'build/libpure-link.a'),
+ str(ROOT / 'build/libpure.a'), '-lm', '-pthread', '-ldl', '-o', str(ROOT / 'build/link-transport')],
+ check=True)
subprocess.run(['cargo', 'build', '--release', '--offline', '--quiet'], cwd=Path(__file__).parent / 'quic', check=True)
with tempfile.TemporaryDirectory(prefix='pure-m5-', dir=args.store) as directory:
work = Path(directory)
diff --git a/serve/deliver.c b/serve/deliver.c
index 05e7f21..b4d9e57 100644
--- a/serve/deliver.c
+++ b/serve/deliver.c
@@ -59,7 +59,7 @@ struct PureWire {
};
/* TLS verifies a receiver's certificate against the system's CAs or the
- * file pure_wire_trust named (serve/tls.h). */
+ * file pure_wire_trust named (link/tls.h). */
static const char *tls_ca;
void pure_wire_trust(const char *ca_file) { tls_ca = ca_file; }
diff --git a/serve/deliver.h b/serve/deliver.h
index f7427c5..71cc38e 100644
--- a/serve/deliver.h
+++ b/serve/deliver.h
@@ -24,7 +24,7 @@ void pure_wire_trust(const char *ca_file);
PureWire *pure_wire_open(const PureModule *m, const char *output, const char *url, const char *source,
char *error, size_t size);
uint32_t pure_wire_output(const PureWire *);
-/* Seal the wire's sends to the receiver's public key (serve/seal.h),
+/* Seal the wire's sends to the receiver's public key (link/seal.h),
* authenticated by own (whose public half is from) when given: each
* request's body, bound to its source, first number and count, goes with
* X-Pure-Seal naming the suite, the session, the seal's number and the
diff --git a/serve/serve.c b/serve/serve.c
index 93406fd..d89fb13 100644
--- a/serve/serve.c
+++ b/serve/serve.c
@@ -162,7 +162,7 @@ typedef struct {
RouteKind route;
PureFault fault; /* a failed event's */
const char *type; /* a view's content type */
- struct LinkJob *link; /* records a link delivered (serve/link.h) */
+ struct LinkJob *link; /* records a link delivered (link/link.h) */
uint64_t heard; /* then the source's last number heard after them */
bool gap; /* and whether they stopped at a gap */
} Applied;