chore: auto-commit uncommitted changes
This commit is contained in:
parent
fef646f6dd
commit
acebec681d
2
Makefile
2
Makefile
|
|
@ -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 := .
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue