docs+site: Nightjar brand, hosted service, infrastructure plan, saved site
Persists the branding/commercial/infrastructure work (previously only in chat and on the live site) into version control. What changed: - docs/naming.md: due-diligence and the decision to brand the product Nightjar (OpenScribe domain/GitHub/trademark collisions; alternatives screened; why Nightjar). - docs/hosted-service.md: the commercial model - plan tiers (Self-host / Cloud Starter / Cloud Pro / Private), indicative pricing, backend mapping, and the build-to-sell roadmap (metering, billing, provisioning, DPA). - docs/infrastructure.md: backend infra plan. Primary option = self-host on the 3x Minisforum MS-02 cluster (one with an RTX 3050 6GB) fronted by a Cloudflare Tunnel, with node roles, the 3050 capacity reality, caveats, and a Hetzner cloud fallback. Provision-later. - site/: reproducible marketing-site source - block content for all 11 pages (rebranded to Nightjar), the navigation, the Contact Form 7 config (honeypot), the ApisCP SOAP helper (tools/apiscp.php, no secret), and a README on how the WordPress site is built and managed via the API. - state/: DECISIONS (Nightjar rebrand, hosted service, MS-02 backend), PROJECT (brand + commercial section), TODO (rename decision, trademark, mailbox, pricing, hosted-service build, infra provisioning). Why: - User asked to save everything to the repo. Captures the product rebrand, the commercialisation plan, and the infrastructure decision so a cold session has the full picture. Notes: - The repo is still named `openscribe`; the product/brand is Nightjar. A full codebase rename is deferred (tracked in TODO + docs/naming.md). - No secrets committed: the ApisCP API key is read from a local scratch file, never the repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a3bdc3c6cf
commit
8fd9b694f8
21 changed files with 592 additions and 0 deletions
79
site/tools/apiscp.php
Normal file
79
site/tools/apiscp.php
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?php declare(strict_types=1);
|
||||
// ApisCP SOAP helper for openscribe.12hobbies.studio provisioning.
|
||||
// Reads the API key from a scratch file so the secret is never embedded here.
|
||||
// Usage:
|
||||
// php apiscp.php --functions [filter] list SOAP operations (optionally grep)
|
||||
// php apiscp.php <module_function> [json-arg ...]
|
||||
// JSON args are decoded (so '["openscribe","/var/www/openscribe"]'-style or bare values work).
|
||||
|
||||
$key = trim((string)file_get_contents('C:/temp/claudetemp/apiscp.key'));
|
||||
if ($key === '') { fwrite(STDERR, "no api key\n"); exit(2); }
|
||||
|
||||
$endpoint = 'https://howler.qsplace.co.uk:2083';
|
||||
// Use a locally-cached WSDL to avoid a second HTTPS fetch; fall back to remote.
|
||||
$wsdl = is_file('C:/temp/claudetemp/apnscp.wsdl')
|
||||
? 'C:/temp/claudetemp/apnscp.wsdl'
|
||||
: $endpoint . '/apnscp.wsdl';
|
||||
// The panel cert is valid, but this PHP build ships no CA bundle, so relax verify.
|
||||
$ctx = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]]);
|
||||
$client = new SoapClient($wsdl, [
|
||||
'connection_timeout' => 30,
|
||||
'location' => $endpoint . '/soap?authkey=' . $key,
|
||||
'uri' => 'urn:net.apnscp.soap',
|
||||
'trace' => 1,
|
||||
'exceptions' => true,
|
||||
'cache_wsdl' => WSDL_CACHE_MEMORY,
|
||||
'stream_context' => $ctx,
|
||||
]);
|
||||
|
||||
$cmd = $argv[1] ?? '';
|
||||
|
||||
// Drive wp-cli with the command string read from a file (avoids all shell-quoting pain
|
||||
// when passing large HTML content). Usage: --wpcli-file <cmdfile> <hostname> <path>
|
||||
if ($cmd === '--wpcli-file') {
|
||||
$command = rtrim((string)file_get_contents($argv[2]), "\r\n");
|
||||
$host = $argv[3];
|
||||
$path = $argv[4] ?? '';
|
||||
try {
|
||||
$res = $client->__soapCall('wordpress_cli', [$command, [], $host, $path]);
|
||||
echo json_encode($res, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
|
||||
exit(isset($res['success']) && !$res['success'] ? 1 : 0);
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, 'ERROR: ' . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cmd === '--functions') {
|
||||
$filter = strtolower($argv[2] ?? '');
|
||||
foreach ($client->__getFunctions() as $fn) {
|
||||
if ($filter === '' || strpos(strtolower($fn), $filter) !== false) {
|
||||
echo $fn, "\n";
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($cmd === '') { fwrite(STDERR, "usage: php apiscp.php <module_function> [json-arg ...]\n"); exit(2); }
|
||||
|
||||
$args = [];
|
||||
foreach (array_slice($argv, 2) as $a) {
|
||||
if (strncmp($a, '@@', 2) === 0) { // @@path -> file contents as a string arg
|
||||
$args[] = (string)file_get_contents(substr($a, 2));
|
||||
continue;
|
||||
}
|
||||
$d = json_decode($a, true);
|
||||
$args[] = ($d === null && $a !== 'null') ? $a : $d;
|
||||
}
|
||||
|
||||
try {
|
||||
$res = $client->__soapCall($cmd, $args);
|
||||
echo json_encode($res, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, 'ERROR: ' . $e->getMessage() . "\n");
|
||||
// surface the raw response for debugging (no secrets in it)
|
||||
if (method_exists($client, '__getLastResponse')) {
|
||||
fwrite(STDERR, "LAST RESPONSE: " . substr((string)$client->__getLastResponse(), 0, 600) . "\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue