merge: bring the open-source readiness work onto the phase-13 branch

Merges gitea-ai/main (65 commits) into the phase-13 branch (419) so one
build carries both lines — the AIUI/assistant/container work and the
open-source readiness work (licensing, the marketplace DID signature layer,
the registry domain migration, the secrets and infrastructure scrub).

Every Rust file auto-merged. The container fixes from this branch and main's
registry-domain migration and node-name genericisation coexist without
manual intervention.

Conflict resolution — all of them were modify/delete, and all were resolved
in main's favour deliberately:

`.planning/**`, `scripts/deploy-to-target.sh` and `scripts/setup-aiui-server.sh`
were deleted by main's `6ba05996` ("security: remove all infrastructure and
internal process material from the repo") and added to .gitignore there.
Keeping this branch's copies would have re-committed internal process and
infrastructure material into a repo being prepared for publication, silently
undoing that cleanup. Resolved with `git rm --cached`, so every file remains
on disk locally and in this branch's history — it is untracked, not lost.
The remaining .planning files this branch added after the merge base were
untracked the same way, so the result is consistent rather than half-tracked.

Container suite 221/221 on the merged tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-08 09:02:32 -04:00
co-authored by Claude Opus 5
542 changed files with 5638 additions and 83054 deletions
+4 -4
View File
@@ -86,7 +86,7 @@ impl PortMap {
/// thing that can publish a port: the FIPS mesh relay bridges the fips0
/// ULA to `127.0.0.1` for a static port list, and it forwarded nbxplorer
/// 32838 — declared `local` and pinned to loopback — to the mesh
/// unauthenticated (archi-dev-box 2026-08-04). Anything that republishes
/// unauthenticated (test node 2026-08-04). Anything that republishes
/// a loopback port must consult this set first.
pub fn is_declared_local(&self, port: u16) -> bool {
self.local.contains(&port)
@@ -135,7 +135,7 @@ fn manifest_icon(manifest: &AppManifest) -> Option<String> {
/// from disk alone made the gate act on policy the node was no longer
/// running: the catalog declared nbxplorer `auth: local` and pinned it to
/// loopback, the stale disk manifest declared nothing, and the gate
/// externally bound a deliberately host-local port (archi-dev-box
/// externally bound a deliberately host-local port (a test node
/// 2026-08-04).
///
/// After the catalog, the first directory that yields a manifest for an app
@@ -268,7 +268,7 @@ fn classify_manifest(manifest: &AppManifest, map: &mut PortMap) {
// and a node's installed manifests always lag the
// repo. Binding those externally published Bitcoin
// RPC across the LAN within seconds of deploy
// (archi-dev-box 2026-08-03). Taking over a port is
// (test node 2026-08-03). Taking over a port is
// opt-in only: `auth: gated`, shipped in the same
// manifest edit as the loopback pin.
if port
@@ -446,7 +446,7 @@ app:
/// This is not hypothetical. `session` is the default, so it is what
/// every un-migrated manifest carries, and a node's installed manifests
/// always lag the repo. An earlier revision gated these regardless of
/// `bind`, and within seconds of deploying to archi-dev-box the daemon
/// `bind`, and within seconds of deploying to a test node the daemon
/// had published Bitcoin's loopback-only RPC 8332 on the LAN, Tailscale
/// and IPv6 addresses. Taking over a port must be opt-in.
#[test]
+1 -1
View File
@@ -155,7 +155,7 @@ pub async fn run(
// RELEASED when its port leaves the gated set — a catalog refresh
// declaring a port `local`/`none` must make the gate let go without a
// daemon restart, or the stale bind keeps republishing a port the
// catalog just withdrew (nbxplorer 32838, archi-dev-box 2026-08-04).
// catalog just withdrew (nbxplorer 32838, a test node 2026-08-04).
let mut held: HashMap<(u16, IpAddr), tokio::task::JoinHandle<()>> = HashMap::new();
let mut interval = tokio::time::interval(SWEEP_INTERVAL);
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
+3 -3
View File
@@ -600,7 +600,7 @@ fn icon_markup(app: &GatedPort) -> String {
// A manifest that names no icon still gets one: the dashboard already
// ships icons named after the app, so fall back to those before
// giving up. Without this EVERY gated app showed a lettermark,
// because no manifest declares metadata.icon (archi-dev-box,
// because no manifest declares metadata.icon (test node,
// 2026-08-05).
.or_else(|| {
icon_candidates(&app.app_id)
@@ -845,7 +845,7 @@ button:active {{ transform:translateY(1px); }}
// NOT X-Frame-Options: DENY. My Apps opens an app in an embedded
// frame, so a blanket DENY made every gated app render as "app is
// not responding" the moment the gate challenged it (reported on
// 100.82.34.38, 2026-08-05). frame-ancestors is the modern control
// a fleet node, 2026-08-05). frame-ancestors is the modern control
// and can be precise: only pages from this same node may frame the
// login, on any port or scheme, which is exactly the dashboard.
// Anything else — another site embedding it to harvest the node
@@ -1003,7 +1003,7 @@ mod tests {
/// The challenge must be framable by this node's own dashboard — My Apps
/// opens apps in an embedded frame, and a blanket `X-Frame-Options: DENY`
/// turned every gated app into "app is not responding" (100.82.34.38,
/// turned every gated app into "app is not responding" (observed on a fleet node,
/// 2026-08-05). It must still be uncacheable, and still refuse to be
/// framed by a foreign origin, which `frame-ancestors` expresses and
/// `X-Frame-Options` cannot.