feat: parliament daemon, login gate, peer context sync (0.9.13)

Login claims the chair, writes a session ticket, and starts a background
service for discovery plus revision-based context sync. Password Realm
opens require --login (or CST_REALM_PASSWORD); --service/--service-stop
manage the resident agent.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-10-28 23:11:23 +08:00
co-authored by Cursor
parent de9ee28773
commit e853abac5e
13 changed files with 769 additions and 12 deletions
+132 -2
View File
@@ -298,7 +298,7 @@ int cst_peer_dial(const char *host, int port, const cst_realm_cert_t *self,
out->remote = remote;
snprintf(out->host, sizeof(out->host), "%s", host);
out->port = port;
os_net_close(s);
out->sock = (intptr_t)s;
return 0;
}
@@ -329,6 +329,136 @@ int cst_peer_accept(intptr_t listen_sock, const cst_realm_cert_t *self,
out->remote = remote;
os_net_peer(s, out->host, sizeof(out->host));
out->port = 0;
os_net_close(s);
out->sock = (intptr_t)s;
return 0;
}
void cst_peer_close(cst_peer_session_t *s) {
if (!s)
return;
if (s->sock != 0 && s->sock != (intptr_t)OS_SOCKET_BAD)
os_net_close((os_socket_t)s->sock);
s->sock = (intptr_t)OS_SOCKET_BAD;
}
static int send_sync_hdr(os_socket_t s, const cst_peer_sync_bundle_t *b,
int timeout_ms) {
cJSON *o = cJSON_CreateObject();
if (!o)
return -1;
cJSON_AddNumberToObject(o, "v", 1);
cJSON_AddStringToObject(o, "op", "sync");
cJSON_AddNumberToObject(o, "revision", (double)b->revision);
cJSON_AddStringToObject(o, "parliament_id", b->parliament_id);
cJSON_AddStringToObject(o, "project_id", b->project_id);
cJSON_AddStringToObject(o, "chair", b->chair_instance);
size_t clen = b->context ? strlen(b->context) : 0;
cJSON_AddNumberToObject(o, "context_len", (double)clen);
char *line = cJSON_PrintUnformatted(o);
cJSON_Delete(o);
if (!line)
return -1;
int rc = send_line(s, line, timeout_ms);
free(line);
if (rc != 0)
return -1;
if (clen > 0 &&
os_net_send_all(s, b->context, clen, timeout_ms) != 0)
return -1;
return 0;
}
static int read_sync_hdr(os_socket_t s, cst_peer_sync_bundle_t *b,
int timeout_ms) {
memset(b, 0, sizeof(*b));
char *line = recv_line(s, timeout_ms);
if (!line)
return -1;
cJSON *o = cJSON_Parse(line);
free(line);
if (!o)
return -1;
const cJSON *op = cJSON_GetObjectItemCaseSensitive(o, "op");
if (!cJSON_IsString(op) || strcmp(op->valuestring, "sync") != 0) {
cJSON_Delete(o);
return -1;
}
const cJSON *v;
v = cJSON_GetObjectItemCaseSensitive(o, "revision");
if (cJSON_IsNumber(v))
b->revision = (unsigned long)v->valuedouble;
v = cJSON_GetObjectItemCaseSensitive(o, "parliament_id");
if (cJSON_IsString(v))
snprintf(b->parliament_id, sizeof(b->parliament_id), "%s", v->valuestring);
v = cJSON_GetObjectItemCaseSensitive(o, "project_id");
if (cJSON_IsString(v))
snprintf(b->project_id, sizeof(b->project_id), "%s", v->valuestring);
v = cJSON_GetObjectItemCaseSensitive(o, "chair");
if (cJSON_IsString(v))
snprintf(b->chair_instance, sizeof(b->chair_instance), "%s",
v->valuestring);
size_t clen = 0;
v = cJSON_GetObjectItemCaseSensitive(o, "context_len");
if (cJSON_IsNumber(v) && v->valuedouble >= 0)
clen = (size_t)v->valuedouble;
cJSON_Delete(o);
if (clen > 2 * 1024 * 1024)
return -1;
if (clen == 0) {
b->context = strdup("");
return b->context ? 0 : -1;
}
char *ctx = malloc(clen + 1);
if (!ctx)
return -1;
size_t got = 0;
while (got < clen) {
long r = os_net_recv(s, ctx + got, clen - got, timeout_ms);
if (r <= 0) {
free(ctx);
return -1;
}
got += (size_t)r;
}
ctx[clen] = 0;
b->context = ctx;
return 0;
}
int cst_peer_sync(cst_peer_session_t *s, cst_peer_sync_bundle_t *local,
int timeout_ms, char *err, size_t err_sz) {
if (err && err_sz)
err[0] = 0;
if (!s || !local || s->sock == (intptr_t)OS_SOCKET_BAD) {
if (err && err_sz)
snprintf(err, err_sz, "bad peer session");
return -1;
}
os_socket_t sock = (os_socket_t)s->sock;
if (send_sync_hdr(sock, local, timeout_ms) != 0) {
if (err && err_sz)
snprintf(err, err_sz, "sync send failed");
return -1;
}
cst_peer_sync_bundle_t remote;
if (read_sync_hdr(sock, &remote, timeout_ms) != 0) {
if (err && err_sz)
snprintf(err, err_sz, "sync recv failed");
return -1;
}
if (remote.revision > local->revision) {
free(local->context);
local->context = remote.context;
remote.context = NULL;
local->revision = remote.revision;
snprintf(local->parliament_id, sizeof(local->parliament_id), "%s",
remote.parliament_id);
snprintf(local->project_id, sizeof(local->project_id), "%s",
remote.project_id);
snprintf(local->chair_instance, sizeof(local->chair_instance), "%s",
remote.chair_instance);
} else {
free(remote.context);
}
return 0;
}