118 lines
4.9 KiB
C++
118 lines
4.9 KiB
C++
// Command line probe over the seal classes, driven by tests/seal_diff.py.
|
|||
|
|
// All text crosses the command line as hex; failures print "ERR <message>" and exit 0.
|
||
|
|
#include <cstdio>
|
||
|
|
#include <filesystem>
|
||
|
|
#include <fstream>
|
||
|
|
#include <iostream>
|
||
|
|
#include <map>
|
||
|
|
#include "config/config.h"
|
||
|
|
#include "seal/seal_factory.h"
|
||
|
|
#include "seal/seal_hmac.h"
|
||
|
|
#include "seal/seal_rotation.h"
|
||
|
|
#include "store/json.h"
|
||
|
|
#include "store/sqldb.h"
|
||
|
|
#include "store/store.h"
|
||
|
|
using namespace detz;
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
|
||
|
|
static std::string unhex(const std::string& h) {
|
||
|
|
std::string o;
|
||
|
|
for (size_t i = 0; i + 1 < h.size(); i += 2) o += static_cast<char>(std::stoi(h.substr(i, 2), nullptr, 16));
|
||
|
|
return o;
|
||
|
|
}
|
||
|
|
static std::string hex(const std::string& s) { return seal::SealHmac::to_hex(s); }
|
||
|
|
|
||
|
|
static Config config_for(const std::string& home) {
|
||
|
|
Config c = Config::FromEnv();
|
||
|
|
c.home = home;
|
||
|
|
c.profilesDir = (fs::path(home) / "profiles").string();
|
||
|
|
c.dbPath = (fs::path(home) / "memory.db").string();
|
||
|
|
c.profile = "default";
|
||
|
|
fs::create_directories(c.profilesDir);
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Writes one row of every sealed kind through the real Store.
|
||
|
|
static void vault_write(const std::string& home, const std::string& pass) {
|
||
|
|
auto s = seal::SealFactory::make(home, pass, false);
|
||
|
|
store::Store st(config_for(home), *s);
|
||
|
|
st.log_event("default", "user", "message", "event with ünïcode and orchid-4421");
|
||
|
|
st.save_secret("api", "secret-value-9", store::Json::loads("{\"kind\": \"token\"}"));
|
||
|
|
st.add_schedule("job", "scheduled prompt zebra", "t", 3600, "2026-01-01T00:00:00+00:00", 60);
|
||
|
|
st.save_system("work", "system text kiwi");
|
||
|
|
st.save_session("work", {store::Json::loads("{\"role\": \"user\", \"content\": \"session text\"}")});
|
||
|
|
st.mcp_save_server(store::Json::loads("{\"name\": \"srv\", \"transport\": \"http\", \"url\": \"http://x\", \"headers\": {\"Authorization\": \"Bearer abc\"}}"));
|
||
|
|
st.upsert_settings({{"backends", store::Json("lbl|http://b|key")}});
|
||
|
|
st.close();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Every sealed column and file, decrypted with this passphrase, as one JSON object.
|
||
|
|
static void vault_dump(const std::string& home, const std::string& pass) {
|
||
|
|
seal::Seal s(home, pass);
|
||
|
|
store::SqlDb db((fs::path(home) / "memory.db").string());
|
||
|
|
auto col = [&](const char* sql) {
|
||
|
|
store::Json out = store::Json::array();
|
||
|
|
for (auto& r : db.execute(sql).rows) {
|
||
|
|
std::string v = r[0].text();
|
||
|
|
out.push(store::Json(store::SealPort::is_sealed(v) ? "S:" + s.unlock(v) : "P:" + v));
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
};
|
||
|
|
store::Json doc = store::Json::object();
|
||
|
|
doc.set("events", col("SELECT text FROM events ORDER BY id"));
|
||
|
|
doc.set("secret_value", col("SELECT value FROM secrets ORDER BY name"));
|
||
|
|
doc.set("secret_meta", col("SELECT meta FROM secrets WHERE meta IS NOT NULL ORDER BY name"));
|
||
|
|
doc.set("schedules", col("SELECT prompt FROM schedules ORDER BY id"));
|
||
|
|
doc.set("settings", col("SELECT value FROM settings WHERE key = 'backends'"));
|
||
|
|
doc.set("mcp_headers", col("SELECT headers_json FROM mcp_servers ORDER BY name"));
|
||
|
|
store::Json files = store::Json::object();
|
||
|
|
std::map<std::string, std::string> sorted;
|
||
|
|
for (auto& e : fs::directory_iterator(fs::path(home) / "profiles")) {
|
||
|
|
std::ifstream in(e.path(), std::ios::binary);
|
||
|
|
std::string raw((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
|
||
|
|
sorted[e.path().filename().string()] = store::SealPort::is_sealed(raw) ? "S:" + s.unlock(raw) : "P:" + raw;
|
||
|
|
}
|
||
|
|
for (auto& [k, v] : sorted) files.set(k, store::Json(v));
|
||
|
|
doc.set("files", files);
|
||
|
|
std::puts(doc.dumps(true).c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(int argc, char** argv) {
|
||
|
|
if (argc < 3) { std::fputs("usage: seal_probe CMD HOME [ARGS]\n", stderr); return 2; }
|
||
|
|
std::string cmd = argv[1], home = argv[2];
|
||
|
|
auto arg = [&](int i) { return i < argc ? std::string(argv[i]) : std::string(); };
|
||
|
|
try {
|
||
|
|
if (cmd == "lock") {
|
||
|
|
seal::Seal s(home, arg(3));
|
||
|
|
std::puts(s.lock(unhex(arg(4))).c_str());
|
||
|
|
} else if (cmd == "unlock") {
|
||
|
|
seal::Seal s(home, arg(3));
|
||
|
|
std::puts(hex(s.unlock(arg(4))).c_str());
|
||
|
|
} else if (cmd == "master") {
|
||
|
|
seal::Seal s(home, arg(3));
|
||
|
|
std::puts(hex(s.master()).c_str());
|
||
|
|
} else if (cmd == "stream") {
|
||
|
|
seal::Seal s(home, arg(3));
|
||
|
|
std::puts(hex(s.stream(unhex(arg(4)), std::stoul(arg(5)))).c_str());
|
||
|
|
} else if (cmd == "factory") {
|
||
|
|
auto s = seal::SealFactory::make(home, arg(3), arg(4) == "1");
|
||
|
|
std::printf("ok enabled=%d default=%d\n", s->enabled(), s->default_key());
|
||
|
|
} else if (cmd == "rotate") {
|
||
|
|
seal::Seal old(home, seal::Seal::kDefaultPassphrase);
|
||
|
|
seal::SealRotation::rotate(seal::SealRotation::Paths::ForHome(home), old, arg(3));
|
||
|
|
std::puts("ok");
|
||
|
|
} else if (cmd == "vault_write") {
|
||
|
|
vault_write(home, arg(3));
|
||
|
|
std::puts("ok");
|
||
|
|
} else if (cmd == "vault_dump") {
|
||
|
|
vault_dump(home, arg(3));
|
||
|
|
} else {
|
||
|
|
std::fputs("unknown command\n", stderr);
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
} catch (const std::exception& e) {
|
||
|
|
std::printf("ERR %s\n", e.what());
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|