28 return "pulp.reload." + std::string(
key_role_name(role)) +
"." + std::string(plugin_id);
35 std::string fpr8(fpr_hex.substr(0, std::min<std::size_t>(8, fpr_hex.size())));
36 return "gen" + std::to_string(generation) + (fpr8.empty() ?
"" :
"-" + fpr8);
42 out.reserve(s.size());
44 if ((c >=
'a' && c <=
'z') || (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9'))
45 out.push_back(
static_cast<char>(std::toupper(
static_cast<unsigned char>(c))));
63 if (repo.empty())
return {};
65 for (
char c : repo) r.push_back(
static_cast<char>(std::tolower(
static_cast<unsigned char>(c))));
66 if (r.size() > 4 && r.substr(r.size() - 4) ==
".git") r.resize(r.size() - 4);
67 if (
auto at = r.find(
'@'); at != std::string::npos) r = r.substr(at + 1);
68 for (
const char* scheme : {
"https://",
"http://",
"ssh://",
"git://"}) {
69 const std::size_t n = std::string_view(scheme).size();
70 if (r.rfind(scheme, 0) == 0) { r = r.substr(n);
break; }
72 if (
auto host = r.find(
"github.com");
host != std::string::npos) {
74 const std::size_t sep = r.find_first_of(
":/",
host);
75 if (sep != std::string::npos) r = r.substr(sep + 1);
77 while (!r.empty() && (r.front() ==
'/' || r.front() ==
':')) r.erase(r.begin());
78 const std::size_t slash = r.find(
'/');
79 if (slash == std::string::npos || slash == 0 || slash + 1 >= r.size())
return {};
80 if (r.find(
'/', slash + 1) != std::string::npos)
return {};
95 return !r.empty() && r !=
"danielraffel/pulp" && r !=
"generous-corp/pulp";
102 s +=
"About to sign swap pack:\n";
103 s +=
" id: " + m.id +
"\n";
104 s +=
" plugin: " + m.plugin_id +
"\n";
105 s +=
" pack version: " + std::to_string(m.pack_version) +
"\n";
107 s +=
" update channel:" + (m.update_channel.empty() ? std::string(
" (none)")
108 :
" " + m.update_channel) +
"\n";
109 s +=
" min host: " + std::to_string(m.min_host_version) +
"\n";
110 s +=
" capabilities: ";
111 if (m.declared_capabilities.empty()) {
112 s +=
"(none — pure UI)";
114 for (std::size_t i = 0; i < m.declared_capabilities.size(); ++i)
115 s += (i ?
", " :
"") + m.declared_capabilities[i];
118 for (
const auto& f : m.files) {
119 const std::string h8 = f.sha256_hex.substr(0, std::min<std::size_t>(8, f.sha256_hex.size()));
130 std::string_view plugin_id) {
133 s +=
"════════════════════════════════════════════════════════════════\n";
134 s += std::string(
" Pulp generated a new ") + std::string(
key_role_name(role)) +
135 " key for " + std::string(plugin_id) +
".\n";
136 s +=
" Stored in your keychain as: " + std::string(keychain_service) +
"\n";
137 s +=
" THIS KEY IS YOUR PLUGIN'S IDENTITY — back it up now.\n";
139 s +=
" • Lose it and you cannot ship signed updates for this plugin.\n";
141 s +=
" • Keep it OFFLINE. Lose it and you cannot revoke a leaked signing key.\n";
142 s +=
" • Leak it and an attacker can sign code as you.\n";
143 s +=
" Pulp will reuse this key; it never silently generates a second one.\n";
144 s +=
"════════════════════════════════════════════════════════════════\n";
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
Sealed swap-pack manifest + per-file integrity.