chore: auto-commit uncommitted changes

This commit is contained in:
James 2026-03-14 18:02:25 -04:00
parent fef646f6dd
commit acebec681d
8 changed files with 90 additions and 112 deletions

View File

@ -16,7 +16,7 @@ WEB_DIR := website
CLI_DIR := cli
APP_BIN := $(APP_DIR)/vault1984
WEB_BIN := $(WEB_DIR)/vault1984-web
CLI_BIN := $(CLI_DIR)/vault1984
CLI_BIN := $(CLI_DIR)/v1984
APP_ENTRY := ./cmd/vault1984
WEB_ENTRY := .

View File

@ -35,7 +35,7 @@ CJSON_DIR := $(VENDOR_DIR)/cjson
CRYPTO_DIR := ../crypto
# Output binary
BIN := vault1984
BIN := v1984
# --- Source files ---

Binary file not shown.

Binary file not shown.

View File

@ -2,11 +2,13 @@
* vault1984 CLI config and key storage
*
* Files stored in ~/.vault1984/ with strict permissions:
* config key=value (host, vault_id)
* token MCP auth token
* config key=value (vault_url, username)
* api_key API authentication key
* l2.key 16-byte L2 encryption key (binary)
*/
#define _POSIX_C_SOURCE 200809L
#include "keystore.h"
#include "util.h"
@ -27,7 +29,7 @@
static int get_config_dir(char *buf, size_t len) {
const char *home = getenv("HOME");
if (!home) home = getenv("USERPROFILE"); /* Windows */
if (!home) home = getenv("USERPROFILE");
if (!home) {
fprintf(stderr, "error: cannot determine home directory\n");
return -1;
@ -70,58 +72,36 @@ static int read_file(const char *path, char *buf, size_t len) {
size_t n = fread(buf, 1, len - 1, f);
fclose(f);
buf[n] = '\0';
/* strip trailing newline */
while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) buf[--n] = '\0';
return 0;
}
int keystore_init(const char *host, const char *agent_token) {
int keystore_init(const char *vault_url, const char *username, const char *api_key) {
char dir[512];
if (get_config_dir(dir, sizeof(dir)) != 0) return 1;
if (ensure_dir(dir) != 0) return 1;
/*
* For now the agent token is the MCP auth token.
* Future: combined token = base64(mcp_token_bytes || encrypted_l2_key)
* We'll split and unwrap when the combined format is defined.
*
* TODO: when combined token format is finalized:
* 1. base64_decode the agent token
* 2. split at known offset mcp_token_bytes + wrapped_l2_key
* 3. unwrap L2 key
* 4. store separately
*/
/* Strip trailing slash from vault_url */
char clean_url[512];
snprintf(clean_url, sizeof(clean_url), "%s", vault_url);
size_t ulen = strlen(clean_url);
while (ulen > 0 && clean_url[ulen - 1] == '/') clean_url[--ulen] = '\0';
/* write config */
/* Write config */
char path[512];
char config_data[512];
/* detect scheme: if host starts with http:// or https://, strip and store */
const char *actual_host = host;
const char *scheme = "https";
if (strncmp(host, "https://", 8) == 0) {
actual_host = host + 8;
scheme = "https";
} else if (strncmp(host, "http://", 7) == 0) {
actual_host = host + 7;
scheme = "http";
}
/* strip trailing slash */
char clean_host[256];
snprintf(clean_host, sizeof(clean_host), "%s", actual_host);
size_t hlen = strlen(clean_host);
while (hlen > 0 && clean_host[hlen - 1] == '/') clean_host[--hlen] = '\0';
snprintf(config_data, sizeof(config_data), "host=%s\nscheme=%s\n", clean_host, scheme);
char config_data[1024];
snprintf(config_data, sizeof(config_data),
"vault_url=%s\nusername=%s\n", clean_url, username);
snprintf(path, sizeof(path), "%s/config", dir);
if (write_file(path, config_data, strlen(config_data)) != 0) return 1;
/* write token */
snprintf(path, sizeof(path), "%s/token", dir);
if (write_file(path, agent_token, strlen(agent_token)) != 0) return 1;
/* Write API key */
snprintf(path, sizeof(path), "%s/api_key", dir);
if (write_file(path, api_key, strlen(api_key)) != 0) return 1;
fprintf(stderr, "vault1984: initialized\n");
fprintf(stderr, " host: %s://%s:1984\n", scheme, clean_host);
fprintf(stderr, "v1984: initialized\n");
fprintf(stderr, " vault: %s\n", clean_url);
fprintf(stderr, " user: %s\n", username);
fprintf(stderr, " config: %s/\n", dir);
return 0;
}
@ -132,16 +112,16 @@ int keystore_load(struct v84_config *cfg) {
char dir[512];
if (get_config_dir(dir, sizeof(dir)) != 0) return -1;
/* read config */
/* Read config */
char path[512];
char config_data[512];
char config_data[1024];
snprintf(path, sizeof(path), "%s/config", dir);
if (read_file(path, config_data, sizeof(config_data)) != 0) {
fprintf(stderr, "error: not initialized. Run: vault1984 init --host <host> --agent <token>\n");
fprintf(stderr, "error: not initialized. Run: v1984 init --vault <url> --user <email> --key <api_key>\n");
return -1;
}
/* parse config key=value lines */
/* Parse key=value lines */
char *line = strtok(config_data, "\n");
while (line) {
char *eq = strchr(line, '=');
@ -149,25 +129,23 @@ int keystore_load(struct v84_config *cfg) {
*eq = '\0';
const char *key = line;
const char *val = eq + 1;
if (strcmp(key, "host") == 0) {
snprintf(cfg->host, sizeof(cfg->host), "%s", val);
} else if (strcmp(key, "vault_id") == 0) {
snprintf(cfg->vault_id, sizeof(cfg->vault_id), "%s", val);
} else if (strcmp(key, "scheme") == 0) {
snprintf(cfg->scheme, sizeof(cfg->scheme), "%s", val);
if (strcmp(key, "vault_url") == 0) {
snprintf(cfg->vault_url, sizeof(cfg->vault_url), "%s", val);
} else if (strcmp(key, "username") == 0) {
snprintf(cfg->username, sizeof(cfg->username), "%s", val);
}
}
line = strtok(NULL, "\n");
}
/* read token */
snprintf(path, sizeof(path), "%s/token", dir);
if (read_file(path, cfg->token, sizeof(cfg->token)) != 0) {
fprintf(stderr, "error: token file missing. Run: vault1984 init --host <host> --agent <token>\n");
/* Read API key */
snprintf(path, sizeof(path), "%s/api_key", dir);
if (read_file(path, cfg->api_key, sizeof(cfg->api_key)) != 0) {
fprintf(stderr, "error: api_key missing. Run: v1984 init --vault <url> --user <email> --key <api_key>\n");
return -1;
}
/* read L2 key if present */
/* Read L2 key if present */
snprintf(path, sizeof(path), "%s/l2.key", dir);
FILE *f = fopen(path, "rb");
if (f) {
@ -176,21 +154,10 @@ int keystore_load(struct v84_config *cfg) {
cfg->has_l2_key = (n == 16);
}
if (!cfg->host[0]) {
fprintf(stderr, "error: config missing host. Run: vault1984 init --host <host> --agent <token>\n");
if (!cfg->vault_url[0]) {
fprintf(stderr, "error: config missing vault_url. Run: v1984 init --vault <url> --user <email> --key <api_key>\n");
return -1;
}
/* default scheme to https */
if (!cfg->scheme[0]) {
snprintf(cfg->scheme, sizeof(cfg->scheme), "https");
}
/* if vault_id not in config, use placeholder from token */
if (!cfg->vault_id[0]) {
/* TODO: derive from L2 key once combined token format is defined */
snprintf(cfg->vault_id, sizeof(cfg->vault_id), "default");
}
return 0;
}

View File

@ -6,16 +6,15 @@
#define V84_KEYSTORE_H
struct v84_config {
char host[256]; /* POP hostname (e.g. use.vault1984.com) */
char vault_id[16]; /* base64(first 8 bytes of L2 key) */
char token[256]; /* MCP auth token */
char scheme[8]; /* "http" or "https" */
unsigned char l2_key[16]; /* L2 encryption key (16 bytes) */
char vault_url[512]; /* e.g. https://use.vault1984.com:1984 */
char username[256]; /* e.g. johan@jongsma.me */
char api_key[256]; /* API auth key (base64) */
unsigned char l2_key[16]; /* L2 encryption key (16 bytes, derived) */
int has_l2_key;
};
/* Initialize config: split agent token, derive vault_id, write files */
int keystore_init(const char *host, const char *agent_token);
/* Initialize config from provided values, write to ~/.vault1984/ */
int keystore_init(const char *vault_url, const char *username, const char *api_key);
/* Load config from ~/.vault1984/ */
int keystore_load(struct v84_config *cfg);

View File

@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "keystore.h"
#include "http.h"
@ -18,61 +19,60 @@
static void usage(void) {
fprintf(stderr,
"vault1984 %s — credential access for AI agents\n"
"v1984 %s — credential access for AI agents\n"
"\n"
"Usage:\n"
" vault1984 init --host <hostname> --agent <token>\n"
" vault1984 get <query> [--json]\n"
" vault1984 list [filter] [--json]\n"
" vault1984 totp <query>\n"
" v1984 init --vault <url> --user <email> --key <api_key>\n"
" v1984 get <query> [--json]\n"
" v1984 list [filter] [--json]\n"
" v1984 totp <query>\n"
" v1984 test-totp <base32-seed>\n"
" v1984 test-crypto\n"
"\n"
"Options:\n"
" --help Show this help\n"
" --version Show version\n"
" --json Output as JSON\n"
"\n"
"Port is always 1984.\n"
"\n"
"Examples:\n"
" vault1984 init --host use.vault1984.com --agent ABCDEF...\n"
" vault1984 get GitHub\n"
" vault1984 totp GitHub\n"
" vault1984 list\n"
" vault1984 list --json\n",
" v1984 init --vault https://use.vault1984.com:1984 --user johan@jongsma.me --key ABCDEF...\n"
" v1984 get twitter.com\n"
" v1984 totp twitter.com\n"
" v1984 list\n"
" v1984 test-totp JBSWY3DPEHPK3PXP\n",
VERSION);
}
/* --- URL builder --- */
static void build_url(char *buf, size_t len, const struct v84_config *cfg, const char *path) {
if (cfg->vault_id[0] && strcmp(cfg->vault_id, "default") != 0) {
snprintf(buf, len, "%s://%s:1984/%s%s", cfg->scheme, cfg->host, cfg->vault_id, path);
} else {
snprintf(buf, len, "%s://%s:1984%s", cfg->scheme, cfg->host, path);
}
snprintf(buf, len, "%s%s", cfg->vault_url, path);
}
/* --- command handlers --- */
static int cmd_init(int argc, char **argv) {
const char *host = NULL;
const char *agent = NULL;
const char *vault = NULL;
const char *user = NULL;
const char *key = NULL;
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--host") == 0 && i + 1 < argc) {
host = argv[++i];
} else if (strcmp(argv[i], "--agent") == 0 && i + 1 < argc) {
agent = argv[++i];
if (strcmp(argv[i], "--vault") == 0 && i + 1 < argc) {
vault = argv[++i];
} else if (strcmp(argv[i], "--user") == 0 && i + 1 < argc) {
user = argv[++i];
} else if (strcmp(argv[i], "--key") == 0 && i + 1 < argc) {
key = argv[++i];
}
}
if (!host || !agent) {
fprintf(stderr, "error: --host and --agent are required\n");
fprintf(stderr, "usage: vault1984 init --host <hostname> --agent <token>\n");
if (!vault || !user || !key) {
fprintf(stderr, "error: --vault, --user, and --key are required\n");
fprintf(stderr, "usage: v1984 init --vault <url> --user <email> --key <api_key>\n");
return 1;
}
return keystore_init(host, agent);
return keystore_init(vault, user, key);
}
static int cmd_list(int argc, char **argv) {
@ -102,7 +102,7 @@ static int cmd_list(int argc, char **argv) {
}
struct v84_response resp;
if (http_get(url, cfg.token, &resp) != 0) {
if (http_get(url, cfg.api_key, &resp) != 0) {
fprintf(stderr, "error: request failed\n");
return 1;
}
@ -179,7 +179,7 @@ static int cmd_get(int argc, char **argv) {
build_url(url, sizeof(url), &cfg, path);
struct v84_response resp;
if (http_get(url, cfg.token, &resp) != 0) {
if (http_get(url, cfg.api_key, &resp) != 0) {
fprintf(stderr, "error: search request failed\n");
return 1;
}
@ -223,7 +223,7 @@ static int cmd_get(int argc, char **argv) {
build_url(url, sizeof(url), &cfg, path);
cJSON_Delete(results);
if (http_get(url, cfg.token, &resp) != 0) {
if (http_get(url, cfg.api_key, &resp) != 0) {
fprintf(stderr, "error: fetch request failed\n");
return 1;
}
@ -318,7 +318,7 @@ static int cmd_totp(int argc, char **argv) {
build_url(url, sizeof(url), &cfg, path);
struct v84_response resp;
if (http_get(url, cfg.token, &resp) != 0) {
if (http_get(url, cfg.api_key, &resp) != 0) {
fprintf(stderr, "error: search request failed\n");
return 1;
}
@ -361,7 +361,7 @@ static int cmd_totp(int argc, char **argv) {
build_url(url, sizeof(url), &cfg, path);
cJSON_Delete(results);
if (http_get(url, cfg.token, &resp) != 0) {
if (http_get(url, cfg.api_key, &resp) != 0) {
fprintf(stderr, "error: TOTP request failed\n");
return 1;
}
@ -632,10 +632,22 @@ int main(int argc, char **argv) {
return cmd_test_crypto();
}
if (strcmp(cmd, "test-totp") == 0 && argc > 2) {
if (strcmp(cmd, "test-totp") == 0) {
if (argc < 3) {
fprintf(stderr, "Generate a TOTP code from a base32 seed.\n\n");
fprintf(stderr, "usage: v1984 test-totp <base32-seed>\n\n");
fprintf(stderr, "example: v1984 test-totp JBSWY3DPEHPK3PXP\n");
fprintf(stderr, " 482901 expires in 17s\n");
return 1;
}
if (jsbridge_init() != 0) { fprintf(stderr, "error: crypto init failed\n"); return 1; }
char *code = jsbridge_totp(argv[2]);
if (code) { printf("%s\n", code); free(code); }
if (code) {
long now = (long)time(NULL);
int remaining = 30 - (int)(now % 30);
printf("%s expires in %ds\n", code, remaining);
free(code);
}
else { fprintf(stderr, "error: TOTP generation failed\n"); }
jsbridge_cleanup();
return code ? 0 : 1;

BIN
cli/v1984 Executable file

Binary file not shown.