Align website and shared guide with three signed Linux channels
This commit is contained in:
1 parent
e98d0d70e6
commit
c0a679d989
16 files changed
+397
-151
No files matched your search
@@ -4,7 +4,7 @@ FrameBridge is free software under the applicable licenses. Everyone retains the
|
||||
|
||||
## What official means
|
||||
|
||||
An official FrameBridge release is built from an identified source revision accepted by the project maintainers. It has a version, source archive, installer, checksums and release signatures. The Debian APT repository publishes signed repository metadata. Official distribution starts at https://betancourt.one/framebridge/. Each release record identifies its signed installer, corresponding-source archive and verification material. A source mirror, when marked published in that record, is available read-only at https://betancourt.one/framebridge/git/framebridge.git. Source branch and tag identities are bound to the signed source archive by the release record; this policy requires release artifact signatures, not an individually signed Git tag.
|
||||
An official FrameBridge release is built from an identified source revision accepted by the project maintainers. It has a version, source archive, installer, checksums and release signatures. The Debian APT repository publishes signed repository metadata. Official distribution starts at https://framebridge.betancourt.one/. The existing GERANAS Gitea repository is https://framebridge.betancourt.one/git/gerardo/framebridge. Each release record identifies the exact source revision, corresponding-source archive, three distribution package profiles, checksums and signatures. The signed APT repository uses independent trixie, noble and mint22 suites for the tested Debian 13, Ubuntu 24.04 and Linux Mint 22.x amd64 profiles. Release artifact signatures identify official downloads; individually signed Git tags are not required by this policy.
|
||||
|
||||
Maintainers control write access, reviewed branches, release tags and signing operations. Public visitors and agents do not receive release-signing or direct protected-branch write access. External contributors propose changes through review.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — command guide
|
||||
|
||||
Version: 0.3.2+private.20261028.2 | Guide schema: 1
|
||||
Version: 0.3.3 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -23,7 +23,7 @@ This reference is generated from the shared CLI/MCP guide. Use the matching inst
|
||||
|
||||
### One interface for people and agents
|
||||
|
||||
FrameBridge provides tools and a shared command-line interface for human operators and authorized agents. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
FrameBridge provides network administration tools and a shared command-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
|
||||
~~~sh
|
||||
fb help
|
||||
@@ -33,10 +33,10 @@ fb --json status
|
||||
|
||||
### Install and connect
|
||||
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2+private.20261028.2_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.3_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topics": {
|
||||
"start": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "start",
|
||||
"title": "Start with FrameBridge",
|
||||
"sections": [
|
||||
{
|
||||
"heading": "One interface for people and agents",
|
||||
"body": "FrameBridge provides tools and a shared command-line interface for human operators and authorized agents. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.",
|
||||
"body": "FrameBridge provides network administration tools and a shared command-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.",
|
||||
"commands": [
|
||||
"fb help",
|
||||
"fb status",
|
||||
@@ -22,9 +22,9 @@
|
||||
},
|
||||
{
|
||||
"heading": "Install and connect",
|
||||
"body": "Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.",
|
||||
"body": "Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.",
|
||||
"commands": [
|
||||
"sudo apt install ./framebridge-expanded_0.3.2+private.20261028.2_amd64.deb",
|
||||
"sudo apt install ./framebridge-expanded_0.3.3_amd64.deb",
|
||||
"fb connect"
|
||||
]
|
||||
},
|
||||
@@ -64,12 +64,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "606e18e34e2b71a8a17ea3e05823a03b49b7a73d3b7a68dfaa725df9a754f16b"
|
||||
"guide_sha256": "23abd7eec3efb77ca160c97fec9b8bacc7040419946e75584ed563a509577a57"
|
||||
},
|
||||
"commands": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "commands",
|
||||
"title": "Command reference",
|
||||
@@ -136,12 +136,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "b765ac3fd5e90518f60f93969c86b4d5f04946cb8604b61a1e462833a7957172"
|
||||
"guide_sha256": "432ab37e7249793cbe72e42d27582d70c2d2c1394ac1f5418b5d3e211e6a7c06"
|
||||
},
|
||||
"connection": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "connection",
|
||||
"title": "Connect and disconnect ChatGPT",
|
||||
@@ -190,12 +190,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "eb8ec2e6d26617b76ccd3d6972233b7fab74072275275c8981d09166bef0d12c"
|
||||
"guide_sha256": "c41d5c436a7039cfacd9cde4933e24c9f7d6235d43e2e9f4fe106f1a4c631764"
|
||||
},
|
||||
"lifecycle": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "lifecycle",
|
||||
"title": "Services, connection intent and boot policy",
|
||||
@@ -256,12 +256,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "eb36c2d78bcda926b896a94385892ce3d479e56dce8a808ad55adfa601472f33"
|
||||
"guide_sha256": "084358c9d1d41e0639b49d01c7e40b613127e3ead77e156cc2624850c7ebdb16"
|
||||
},
|
||||
"nodes": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "nodes",
|
||||
"title": "Central and node operation",
|
||||
@@ -310,12 +310,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "fc9d260c0f0b4e4f84087297396044058d7ec2b8821391dae5187eb58683cad7"
|
||||
"guide_sha256": "ec2adb6ba2cb36e3747bcb4975a6b777ad88d4fe2b9b1cfb1a2f6ec1e8dab43a"
|
||||
},
|
||||
"terminals": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "terminals",
|
||||
"title": "Persistent Bash terminals",
|
||||
@@ -376,12 +376,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "162f069fbdb4584639be386163239530c1b9b883972ab212f5bccd47f6639e82"
|
||||
"guide_sha256": "0aba231493d9ecae071ecdf5197440e0714393364948079fc4d163ff70cc67e3"
|
||||
},
|
||||
"screens": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "screens",
|
||||
"title": "Authentic screen observation and input",
|
||||
@@ -431,12 +431,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "1734ee9890fe63d60fe008879c75804fa0c755c0396153a4be2e8228fd790316"
|
||||
"guide_sha256": "eeaf9cfed9e7a84d59a727c4388b0869b02a6ed870647e87b333d318a3584c9a"
|
||||
},
|
||||
"diagnostics": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "diagnostics",
|
||||
"title": "Status and diagnosis",
|
||||
@@ -481,12 +481,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "2899db14de0686a2dc50a2b22edf35d97d7d53e162300194fe57c126dde395dc"
|
||||
"guide_sha256": "77a65897aacfeaa2b06dd184ac78d366d28980847b0cbdd1429c889e0e2898a6"
|
||||
},
|
||||
"security": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "security",
|
||||
"title": "Authorization, distribution and safe operation",
|
||||
@@ -521,12 +521,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "addecd0420c18951683f32bdc05a9ab7d00a1b2150ecd6087bd99b784196696f"
|
||||
"guide_sha256": "f257fcbbdec39395a15655b0a5123fb385ce7eaba2a256de07a5da65eab3ec8e"
|
||||
},
|
||||
"compatibility": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2+private.20261028.2",
|
||||
"version": "0.3.3",
|
||||
"guide_schema": 1,
|
||||
"topic": "compatibility",
|
||||
"title": "Compatibility with earlier commands",
|
||||
@@ -569,8 +569,8 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "0dcbf347bfba8b0cff176d5c96bcf1a95f2c83fc6398559d54ac3b4c83d74b0a"
|
||||
"guide_sha256": "9056f77b27f326bebfdbb54dd10b748d6315797b07afe1aee29f42d6210dae36"
|
||||
}
|
||||
},
|
||||
"guide_sha256": "1f259d089526e7e38f1732afbc4ceae2af62ec36c8630452fd8f4ef4ccb90069"
|
||||
"guide_sha256": "5dc6ed2744e53d7d2ce1f471e2ab94d50f9c52f83ae85abe495bacad34b1b96c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — command guide
|
||||
|
||||
Version: 0.3.2+private.20261028.2 | Guide schema: 1
|
||||
Version: 0.3.3 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -23,7 +23,7 @@ This reference is generated from the shared CLI/MCP guide. Use the matching inst
|
||||
|
||||
### One interface for people and agents
|
||||
|
||||
FrameBridge provides tools and a shared command-line interface for human operators and authorized agents. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
FrameBridge provides network administration tools and a shared command-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
|
||||
~~~sh
|
||||
fb help
|
||||
@@ -33,10 +33,10 @@ fb --json status
|
||||
|
||||
### Install and connect
|
||||
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2+private.20261028.2_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.3_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — Debian, Ubuntu and Linux Mint installation and first use
|
||||
|
||||
Version: 0.3.2+private.20261028.2 | Guide schema: 1
|
||||
Version: 0.3.3 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -23,7 +23,7 @@ This reference is generated from the shared CLI/MCP guide. Use the matching inst
|
||||
|
||||
### One interface for people and agents
|
||||
|
||||
FrameBridge provides tools and a shared command-line interface for human operators and authorized agents. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
FrameBridge provides network administration tools and a shared command-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
|
||||
~~~sh
|
||||
fb help
|
||||
@@ -33,10 +33,10 @@ fb --json status
|
||||
|
||||
### Install and connect
|
||||
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2+private.20261028.2_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.3_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH FRAMEBRIDGE 1 "2026-10-27" "FrameBridge 0.3.2+private.20261028.2" "User Commands"
|
||||
.TH FRAMEBRIDGE 1 "2026-10-28" "FrameBridge 0.3.3" "User Commands"
|
||||
.SH NAME
|
||||
framebridge, fb \- use local Linux tools and enrolled nodes with ChatGPT
|
||||
.SH SYNOPSIS
|
||||
@@ -10,7 +10,7 @@ The names fb and framebridge execute the same program.
|
||||
The tools run on the selected Linux computers; the ChatGPT model remains hosted.
|
||||
.SH START WITH FRAMEBRIDGE
|
||||
.SS One interface for people and agents
|
||||
FrameBridge provides tools and a shared command\-line interface for human operators and authorized agents. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
FrameBridge provides network administration tools and a shared command\-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.
|
||||
.PP
|
||||
.nf
|
||||
fb help
|
||||
@@ -18,10 +18,10 @@ fb status
|
||||
fb \-\-json status
|
||||
.fi
|
||||
.SS Install and connect
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
.PP
|
||||
.nf
|
||||
sudo apt install ./framebridge\-expanded_0.3.2+private.20261028.2_amd64.deb
|
||||
sudo apt install ./framebridge\-expanded_0.3.3_amd64.deb
|
||||
fb connect
|
||||
.fi
|
||||
.SS Updating and removal
|
||||
|
||||
@@ -4,19 +4,10 @@ from . import __version__
|
||||
|
||||
GUIDE = {'start': {'title': 'Start with FrameBridge',
|
||||
'sections': [{'heading': 'One interface for people and agents',
|
||||
'body': 'FrameBridge provides tools and a shared command-line interface '
|
||||
'for human operators and authorized agents. fb is the short name '
|
||||
'for framebridge; both run the same program. The tools run on '
|
||||
'your Linux computers. The ChatGPT model remains hosted. Read '
|
||||
'this guide for the installed version before operating; confirm '
|
||||
'the receiver, target and available capabilities. Use the '
|
||||
'installed CLI and tools for normal work. Edit software only when '
|
||||
'the owner explicitly requests development, using a separate '
|
||||
'source clone and reviewable changes rather than rewriting the '
|
||||
'installed program.',
|
||||
'body': 'FrameBridge provides network administration tools and a shared command-line interface for a home Linux network with one human administrator and authorized agents. Desktop access is an auxiliary tool. fb is the short name for framebridge; both run the same program. The tools run on your Linux computers. The ChatGPT model remains hosted. Read this guide for the installed version before operating; confirm the receiver, target and available capabilities. Use the installed CLI and tools for normal work. Edit software only when the owner explicitly requests development, using a separate source clone and reviewable changes rather than rewriting the installed program.',
|
||||
'commands': ['fb help', 'fb status', 'fb --json status']},
|
||||
{'heading': 'Install and connect',
|
||||
'body': 'Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.',
|
||||
'body': 'Install the package built for your exact Debian 13, Ubuntu 24.04 or Linux Mint 22.x amd64 profile. These are the three release profiles; other distributions and architectures are outside this release. Keep the distribution system Python: 3.13 on Debian 13, 3.12 on Ubuntu 24.04 and Mint 22.x. Use the matching signed APT suite: trixie, noble or mint22. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.',
|
||||
'commands': ["sudo apt install ./framebridge-expanded_" + __version__ + "_amd64.deb", 'fb connect']},
|
||||
{'heading': 'Updating and removal',
|
||||
'body': 'Install an official update through the normal package manager when that update has been published. Normal removal stops package-owned managed processes and can retain the user profile and encrypted connection material; it is not a credential-forgetting operation. On systemd 255, package removal also revokes the administrative boot-credential grant. After reinstalling, run fb connect interactively to provision that grant again; retained files alone do not restore startup. Use the explicit credentials forget action first when that is your intended outcome. Do not assume purge erases user-profile data. An explicit downgrade also revokes administrative helper grants before the older package files replace the helper. Helper-backed profiles require a new interactive connect with a compatible package before managed startup can be restored.',
|
||||
|
||||
@@ -26,7 +26,7 @@ def markdown(data, title):
|
||||
def roff(text):
|
||||
return text.replace('\\','\\e').replace('-','\\-')
|
||||
def man(data):
|
||||
lines=['.TH FRAMEBRIDGE 1 "2026-10-27" "FrameBridge '+__version__+'" "User Commands"',
|
||||
lines=['.TH FRAMEBRIDGE 1 "2026-10-28" "FrameBridge '+__version__+'" "User Commands"',
|
||||
'.SH NAME','framebridge, fb \\- use local Linux tools and enrolled nodes with ChatGPT',
|
||||
'.SH SYNOPSIS','.B framebridge',
|
||||
'[\\-\\-json] [\\-\\-state\\-dir DIRECTORY] COMMAND [ARGUMENTS]',
|
||||
|
||||
@@ -2,3 +2,5 @@ dist/
|
||||
qa/
|
||||
release.json
|
||||
__pycache__/
|
||||
assets/framebridge-preview-0.3.1-original-r4.mp4
|
||||
assets/framebridge-preview-0.3.1-original-r4.jpg
|
||||
+47
-1
@@ -1,3 +1,49 @@
|
||||
# FrameBridge website source
|
||||
|
||||
The website implementation follows the project GPL-3.0-only license. Project video is delivered separately. Copy release.example.json to release.json, add the supplied conceptual preview to assets/framebridge-preview.mp4, then run python3 -I -S build_site.py and python3 -I -S qa_site.py. The generator reads ../source/docs/COMMAND-GUIDE.json in this source layout. Production signing metadata must be supplied by a maintainer; never fabricate fingerprints or signatures.
|
||||
The original visual design and renderer are retained. The main workflow is
|
||||
administration of a home Linux network by one human administrator. Compatible
|
||||
desktop access is an auxiliary tool.
|
||||
|
||||
The canonical source repository is the existing GERANAS Gitea project
|
||||
https://framebridge.betancourt.one/git/gerardo/framebridge. CLOUD provides the
|
||||
existing HTTPS reverse proxy; GERANAS serves the website, downloads and APT
|
||||
repository. Do not substitute a static Git mirror or another hosting backend.
|
||||
|
||||
Copy release.example.json to release.json, then fill it from the exact verified
|
||||
release inventory. Its three profiles are Debian 13 amd64/Python 3.13 (trixie),
|
||||
Ubuntu 24.04 amd64/Python 3.12 (noble), and Linux Mint 22.x amd64/Python 3.12
|
||||
(mint22). A preparing catalog offers no new download or installation commands.
|
||||
Use ready only after verifying all DEBs, corresponding source, dependency
|
||||
source archives, signatures, checksums and the official public fingerprint.
|
||||
Metadata describes release artifacts; it is not itself evidence that a public
|
||||
hostname is operational.
|
||||
|
||||
Project media is delivered separately. Copy the approved original R4 MP4 and
|
||||
JPEG into assets under their original filenames. The renderer verifies these
|
||||
identities before a ready build:
|
||||
|
||||
- framebridge-preview-0.3.1-original-r4.mp4:
|
||||
b2a8b0b302063edbe05d84fb6dadb7f303a6f0f1b6d79dc151287df4671adc5b
|
||||
- framebridge-preview-0.3.1-original-r4.jpg:
|
||||
2e867d4b6a22f81ecbefa293a88799b9e3c93f02aa7d1090c7f0a0dbfe44dc9f
|
||||
|
||||
The approved animation and original music remain unchanged. The video is
|
||||
identified as a 0.3.1 conceptual demonstration; it does not claim to be a live
|
||||
recording of the current release.
|
||||
|
||||
Run:
|
||||
|
||||
python3 -I -S build_site.py
|
||||
python3 -I -S qa_site.py
|
||||
python3 -I -S test_release_catalog.py
|
||||
|
||||
The generator reads ../source/docs/COMMAND-GUIDE.json and requires its version
|
||||
to match the release. QA checks local links/assets, loopback HTTP, the guarded
|
||||
ready renderer with a disposable synthetic inventory, and syntax of all three
|
||||
APT setup blocks. The test fixtures never sign, publish or install anything.
|
||||
Perform final HTTPS, Gitea clone and isolated APT verification through the
|
||||
actual public domain before reporting publication complete.
|
||||
|
||||
The website follows the project's GPL-3.0-only license. Third-party materials
|
||||
retain their own licenses and notices. Support is voluntary; the contact and
|
||||
PayPal account supplied by the project owner is gerardobh1128@gmail.com.
|
||||
+20
-64
@@ -1,25 +1,25 @@
|
||||
#!/usr/bin/python3
|
||||
"""Build the self-hosted FrameBridge website with the Python standard library."""
|
||||
from pathlib import Path
|
||||
import html,json,re,shutil,sys
|
||||
import html,json,re,shutil,sys,runpy,hashlib
|
||||
|
||||
BASE=Path(__file__).resolve().parent
|
||||
ROOT=BASE.parent
|
||||
DIST=BASE/'dist'
|
||||
META=json.loads((BASE/'release.json').read_text())
|
||||
META.setdefault('git_url','/git/framebridge/framebridge')
|
||||
META.setdefault('git_clone_url','https://framebridge.betancourt.one/git/framebridge/framebridge.git')
|
||||
META.setdefault('git_url','/git/gerardo/framebridge')
|
||||
META.setdefault('git_clone_url','https://framebridge.betancourt.one/git/gerardo/framebridge.git')
|
||||
E=lambda value:html.escape(str(value),quote=True)
|
||||
READY=META.get('state')=='ready'
|
||||
if READY:
|
||||
assert re.fullmatch(r'[a-fA-F0-9]{64}',META.get('sha256','')),'Ready release requires the exact package SHA-256.'
|
||||
assert re.fullmatch(r'[a-fA-F0-9]{40}|[a-fA-F0-9]{64}',META.get('signing_fingerprint','')),'Ready release requires a public signing fingerprint.'
|
||||
assert META.get('source_archive_url') and META.get('package_signature_url') and META.get('size_bytes'),'Ready release requires source, signature and size metadata.'
|
||||
deps=META.get('dependency_sources',[])
|
||||
assert len(deps)>=2 and all(item.get('url','').startswith('/downloads/') and re.fullmatch(r'[a-fA-F0-9]{64}',item.get('sha256','')) and item.get('size_bytes') for item in deps),'Ready release requires all dependency-source archives and their exact checksums.'
|
||||
for name in ['git_url','package_url','package_signature_url','checksums_url','checksums_signature_url','source_archive_url','signing_key_url']:
|
||||
value=META.get(name,'')
|
||||
assert not value or value.startswith('/') or value.startswith('https://framebridge.betancourt.one/'),'Only project-owned HTTPS or root-relative artifact URLs are allowed.'
|
||||
CAT=runpy.run_path(str(BASE/'release_catalog.py'))
|
||||
CAT['validate'](META,READY)
|
||||
VIDEO=CAT['VIDEO'];POSTER=CAT['POSTER']
|
||||
for name,expected in [(VIDEO,CAT['VIDEO_SHA256']),(POSTER,CAT['POSTER_SHA256'])]:
|
||||
asset=BASE/'assets'/name
|
||||
if asset.exists():
|
||||
assert hashlib.sha256(asset.read_bytes()).hexdigest()==expected,'Approved original media changed.'
|
||||
elif READY:
|
||||
raise ValueError('Approved original media is required before publication.')
|
||||
for path in ['assets','docs','download','blog/2026-10-26-opening-framebridge','community','support']:
|
||||
(DIST/path).mkdir(parents=True,exist_ok=True)
|
||||
for p in (BASE/'src').iterdir():
|
||||
@@ -48,59 +48,15 @@ def code(text):
|
||||
return '<div class="codebox"><pre><code>'+E(text)+'</code></pre></div>'
|
||||
|
||||
release_label='Official release' if READY else 'Release preparation'
|
||||
home='''<main id="content"><div class="wrap"><section class="hero"><div><div class="eyebrow">Linux systems · Your ChatGPT account</div><h1>Your computers.<br>A working conversation.</h1><p class="lead">Bring commands, persistent terminals and a compatible desktop into your conversation with ChatGPT. Work on Linux systems you control, through one bridge.</p><div class="actions"><a class="button primary" href="/download/">Get FrameBridge <span aria-hidden="true"> ↗</span></a><a class="button" href="#preview">Watch the concept</a></div><p class="micro">GNU GPLv3 · Debian 13 · Manual connection to your own account</p></div><div><div class="terminal" aria-label="Illustrative command workflow"><div class="terminal-top"><span>FRAMEBRIDGE / EXAMPLE WORKFLOW</span><span aria-hidden="true">— □ ×</span></div><div class="terminal-body"><div class="term-row"><p class="term-command"><span>$</span>fb connect</p><div class="term-note">Connect your own account through the guided terminal flow.</div></div><div class="term-row"><p class="term-command"><span>$</span>fb status</p><div class="term-note">See tools, connection and startup state in one place.</div></div><div class="term-row"><p class="term-command"><span>$</span>fb console</p><div class="term-note">Return to your work view: Bash and FBTERM activity.</div></div></div></div><p class="terminal-caption">Illustrative commands. This is not a screen capture.</p></div></section><div class="strip rule"><span>Local execution</span><span>Persistent terminals</span><span>Graphical + text tools</span><span>Central + nodes</span></div></div>
|
||||
<section class="section rule" id="preview"><div class="wrap"><div class="section-head"><div><p class="section-label">01 / A conversation takes shape</p><h2>From a blinking cursor<br>to a connected workflow.</h2></div><p>A conceptual look at independent tasks, consoles and connected computers. The software runs your tools on your systems; the ChatGPT model remains hosted.</p></div><div class="video-shell"><video controls preload="metadata" poster="/assets/video-poster.svg" aria-label="FrameBridge conceptual preview animation"><source src="/assets/framebridge-preview.mp4" type="video/mp4"><p>Your browser cannot play this video. <a href="/assets/framebridge-preview.mp4">Download the MP4 preview.</a></p></video></div><p class="video-caption"><strong>Conceptual animation — not a screen recording.</strong><span>Original RC7 concept preview. Features and commands are documented for the current release below.</span></p></div></section>
|
||||
<section class="section rule"><div class="wrap workflow"><div><p class="section-label">02 / The bridge</p><h2>Start with one machine.<br>Extend when you need to.</h2><p class="lead">A standalone computer can provide local tools. A central installation can also connect approved nodes.</p><div class="architecture"><div class="architecture-title">Where work happens</div><div class="architecture-line"><span>Your ChatGPT account</span><span class="arrow" aria-hidden="true">→</span><span>FrameBridge</span><span class="arrow" aria-hidden="true">→</span><span>Your Linux systems</span></div></div><p class="note">No phone pairing is required. Initial account consent and credentials stay in the normal browser or hidden terminal prompts.</p></div><ol class="workflow-list"><li><span class="step-no">01</span><div><h3>Install and connect</h3><p>Install the Debian package, then use the guided text interface to connect your own account. Advanced role setup is optional for ordinary standalone use.</p></div></li><li><span class="step-no">02</span><div><h3>Choose the right tool</h3><p>Use FBEXEC for commands, persistent terminals for ongoing shell work, and graphical CUA when a compatible desktop is available.</p></div></li><li><span class="step-no">03</span><div><h3>Keep the context</h3><p>Return to a live terminal session without rebuilding its shell state between calls. Running processes do not survive an operating-system reboot.</p></div></li><li><span class="step-no">04</span><div><h3>Add approved nodes</h3><p>Use the central instance for ChatGPT transport and select enrolled nodes explicitly. Nodes do not need a second ChatGPT tunnel.</p></div></li></ol></div></section>
|
||||
<section class="section rule"><div class="wrap"><p class="section-label">03 / A practical toolset</p><div class="feature-row"><h3>Execution</h3><p>Run authorized shell tasks on the selected machine and inspect their recorded results.</p><code>FBEXEC</code></div><div class="feature-row"><h3>Terminals</h3><p>Keep a real Bash session across calls. Inspect text, send input and return to ongoing work.</p><code>FBTERM / fb tty</code></div><div class="feature-row"><h3>Desktop and text</h3><p>Discover compatible graphical CUA automatically. Use text-terminal tools when a graphical desktop is unavailable.</p><code>CUA / CUA-TTY</code></div><div class="feature-row"><h3>People and agents</h3><p>The same command-line interface serves human operators and authorized ChatGPT agents, with discoverable usage guidance.</p><code>fb / framebridge</code></div><p class="note">Independent FBEXEC tasks have been used together in the laboratory when their objectives and changes did not overlap. This is observed practice, not an isolation guarantee. Use one controller at a time for each shared graphical desktop.</p></div></section>
|
||||
home='''<main id="content"><div class="wrap"><section class="hero"><div><div class="eyebrow">Home Linux networks · One administrator</div><h1>Your Linux network.<br>One place to administer it.</h1><p class="lead">Administer your own home Linux network through ChatGPT: inspect equipment, diagnose connections, manage services and run commands across enrolled computers. One administrator, one bridge.</p><div class="actions"><a class="button primary" href="/download/">Get FrameBridge <span aria-hidden="true"> ↗</span></a><a class="button" href="#preview">Watch the concept</a></div><p class="micro">GPL-3.0-only · Debian 13 · Ubuntu 24.04 · Linux Mint 22.x · amd64</p></div><div><div class="terminal" aria-label="Illustrative command workflow"><div class="terminal-top"><span>FRAMEBRIDGE / EXAMPLE WORKFLOW</span><span aria-hidden="true">— □ ×</span></div><div class="terminal-body"><div class="term-row"><p class="term-command"><span>$</span>fb connect</p><div class="term-note">Connect your own account through the guided terminal flow.</div></div><div class="term-row"><p class="term-command"><span>$</span>fb status</p><div class="term-note">See tools, connection and startup state in one place.</div></div><div class="term-row"><p class="term-command"><span>$</span>fb console</p><div class="term-note">Return to your work view: Bash and FBTERM activity.</div></div></div></div><p class="terminal-caption">Illustrative commands. This is not a screen capture.</p></div></section><div class="strip rule"><span>Local execution</span><span>Persistent terminals</span><span>Network administration</span><span>Central + nodes</span></div></div>
|
||||
<section class="section rule" id="preview"><div class="wrap"><div class="section-head"><div><p class="section-label">01 / A conversation takes shape</p><h2>From a blinking cursor<br>to a connected workflow.</h2></div><p>A conceptual look at independent tasks, consoles and connected computers. The software runs your tools on your systems; the ChatGPT model remains hosted.</p></div><div class="video-shell"><video controls preload="metadata" poster="/assets/framebridge-preview-0.3.1-original-r4.jpg" aria-label="FrameBridge conceptual preview animation"><source src="/assets/framebridge-preview-0.3.1-original-r4.mp4" type="video/mp4"><p>Your browser cannot play this video. <a href="/assets/framebridge-preview-0.3.1-original-r4.mp4">Download the MP4 preview.</a></p></video></div><p class="video-caption"><strong>Conceptual animation — not a screen recording.</strong><span>Approved 0.3.1 concept preview with its original music. Current release packages and commands are documented separately.</span></p></div></section>
|
||||
<section class="section rule"><div class="wrap workflow"><div><p class="section-label">02 / The bridge</p><h2>Start with one machine.<br>Extend when you need to.</h2><p class="lead">A standalone computer can provide local tools. A central installation can also connect approved nodes.</p><div class="architecture"><div class="architecture-title">Where work happens</div><div class="architecture-line"><span>Your ChatGPT account</span><span class="arrow" aria-hidden="true">→</span><span>FrameBridge</span><span class="arrow" aria-hidden="true">→</span><span>Your Linux systems</span></div></div><p class="note">No phone pairing is required. Initial account consent and credentials stay in the normal browser or hidden terminal prompts.</p></div><ol class="workflow-list"><li><span class="step-no">01</span><div><h3>Install and connect</h3><p>Install the package for your distribution, then use the guided text interface to connect your own account. Advanced role setup is optional for ordinary standalone use.</p></div></li><li><span class="step-no">02</span><div><h3>Choose the right tool</h3><p>Use FBEXEC for commands, persistent terminals for ongoing shell work, and graphical CUA when a compatible desktop is available.</p></div></li><li><span class="step-no">03</span><div><h3>Keep the context</h3><p>Return to a live terminal session without rebuilding its shell state between calls. Running processes do not survive an operating-system reboot.</p></div></li><li><span class="step-no">04</span><div><h3>Add approved nodes</h3><p>Use the central instance for ChatGPT transport and select enrolled nodes explicitly. Nodes do not need a second ChatGPT tunnel.</p></div></li></ol></div></section>
|
||||
<section class="section rule"><div class="wrap"><p class="section-label">03 / A practical toolset</p><div class="feature-row"><h3>Execution</h3><p>Run authorized shell tasks on the selected machine and inspect their recorded results.</p><code>FBEXEC</code></div><div class="feature-row"><h3>Terminals</h3><p>Keep a real Bash session across calls. Inspect text, send input and return to ongoing work.</p><code>FBTERM / fb tty</code></div><div class="feature-row"><h3>Desktop and text</h3><p>Use compatible graphical and text access as auxiliary tools for your own equipment. Commands, diagnostics and network administration remain the main workflow.</p><code>CUA / CUA-TTY</code></div><div class="feature-row"><h3>People and agents</h3><p>The same command-line interface serves human operators and authorized ChatGPT agents, with discoverable usage guidance.</p><code>fb / framebridge</code></div><p class="note">Independent FBEXEC tasks have been used together in the laboratory when their objectives and changes did not overlap. This is observed practice, not an isolation guarantee. Use one controller at a time for each shared graphical desktop.</p></div></section>
|
||||
<section class="section rule" id="download"><div class="wrap"><div class="release-panel"><div><p class="section-label">04 / Start working</p><h2>Free software.<br>Verifiable releases.</h2><p>Read the code, adapt it and contribute. Official downloads are identified by their published checksums and release signatures.</p><div class="actions"><a class="button primary" href="/download/">Download & verify</a><a class="button" href="/docs/">Read the guide</a></div></div><ul class="release-facts"><li><span>Version</span><strong>'''+E(META['version'])+'''</strong></li><li><span>Platform</span><strong>'''+E(META['distribution'])+'''</strong></li><li><span>License</span><strong>GNU GPLv3 only</strong></li><li><span>Channel</span><strong>'''+E(release_label)+'''</strong></li></ul></div></div></section>
|
||||
<section class="section rule"><div class="wrap community"><div><p class="section-label">05 / Built in the open</p><h2>A small project.<br>An open invitation.</h2><p>Good tools improve through use, careful fixes and clear documentation. Report a reproducible issue, improve the guide or propose a change in Git.</p><a class="text-link" href="/community/">How to contribute <span aria-hidden="true">→</span></a></div><div><p class="section-label">Development journal</p><a class="news-link" href="/blog/2026-10-26-opening-framebridge/"><time datetime="2026-10-26">26 October 2026 · Project notes</time><strong>Opening FrameBridge: a shared interface for people and agents.</strong><span>Read the development note →</span></a></div></div></section></main>'''
|
||||
(DIST/'index.html').write_text(page('Your computers. A working conversation.',home))
|
||||
(DIST/'index.html').write_text(page('Administer your home Linux network',home))
|
||||
|
||||
notice='' if READY else '<div class="release-notice"><strong>Release preparation.</strong> Download and installation commands will appear here once the package, corresponding source and signing information have been finalized. No placeholder checksum or key should be trusted.</div>'
|
||||
facts='<dl class="download-summary"><dt>Package</dt><dd>'+E(META['package_filename'])+'</dd><dt>Version</dt><dd>'+E(META['version'])+'</dd><dt>Platform</dt><dd>'+E(META['distribution'])+' · '+E(META['architecture'])+'</dd><dt>License</dt><dd>GNU GPLv3 only, with separate third-party notices</dd>'
|
||||
if READY:facts+='<dt>Package SHA-256</dt><dd class="hash">'+E(META['sha256'])+'</dd><dt>Signing-key fingerprint</dt><dd class="hash">'+E(META['signing_fingerprint'])+'</dd>'
|
||||
facts+='</dl>'
|
||||
manual=''
|
||||
apt=''
|
||||
if READY:
|
||||
def absolute(value):
|
||||
return value if value.startswith('https://') else META['site_url']+value
|
||||
manual_commands='''sudo apt install ca-certificates curl gnupg
|
||||
(
|
||||
set -eu
|
||||
verify_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$verify_dir"' EXIT
|
||||
curl -fsSL "'''+absolute(META['signing_key_url'])+'''" -o "$verify_dir/project.asc"
|
||||
expected="'''+META['signing_fingerprint'].upper()+'''"
|
||||
actual="$(gpg --show-keys --with-colons "$verify_dir/project.asc" 2>/dev/null | awk -F: '$1=="fpr" {print $10; exit}')"
|
||||
test "$actual" = "$expected" || { printf '%s\\n' "Signing-key fingerprint mismatch."; exit 1; }
|
||||
gpg --batch --dearmor --output "$verify_dir/project.gpg" "$verify_dir/project.asc"
|
||||
curl -fsSL "'''+absolute(META['package_url'])+'''" -o "$verify_dir/'''+META['package_filename']+'''"
|
||||
curl -fsSL "'''+absolute(META['package_signature_url'])+'''" -o "$verify_dir/package.asc"
|
||||
printf '%s %s\\n' "'''+META['sha256']+'''" "$verify_dir/'''+META['package_filename']+'''" | sha256sum --check
|
||||
gpgv --keyring "$verify_dir/project.gpg" "$verify_dir/package.asc" "$verify_dir/'''+META['package_filename']+'''"
|
||||
sudo apt install "$verify_dir/'''+META['package_filename']+'''"
|
||||
)'''
|
||||
manual='<div class="actions"><a class="button primary" href="'+E(META['package_url'])+'" download>Download .deb</a><a class="button" href="'+E(META['source_archive_url'])+'">Corresponding source</a></div><p class="micro"><a href="'+E(META['package_signature_url'])+'">Package signature</a> · <a href="'+E(META['checksums_url'])+'">SHA256SUMS</a> · <a href="'+E(META['checksums_signature_url'])+'">Signed checksums</a></p><h2>Download, verify and install</h2><p>This complete terminal flow checks the expected signing-key fingerprint, package checksum and detached signature before asking APT to install the package and resolve its dependencies.</p>'+code(manual_commands)
|
||||
source_items=''.join('<li><a href="'+E(item['url'])+'">'+E(item['label'])+'</a><br><code>'+E(item['filename'])+'</code><br><span class="hash">SHA-256: '+E(item['sha256'])+'</span></li>' for item in META['dependency_sources'])
|
||||
manual+='<h2>Complete source materials</h2><p>The matching FrameBridge source archive above and the following dependency-source materials are distributed together for this release. Preserve their license notices when redistributing.</p><ul>'+source_items+'</ul>'
|
||||
apt_commands='''sudo apt install ca-certificates curl gnupg
|
||||
(
|
||||
set -eu
|
||||
key_file="$(mktemp)"
|
||||
trap 'rm -f "$key_file"' EXIT
|
||||
curl -fsSL "'''+absolute(META['signing_key_url'])+'''" -o "$key_file"
|
||||
expected="'''+META['signing_fingerprint'].upper()+'''"
|
||||
actual="$(gpg --show-keys --with-colons "$key_file" 2>/dev/null | awk -F: '$1=="fpr" {print $10; exit}')"
|
||||
test "$actual" = "$expected" || { printf '%s\\n' "Signing-key fingerprint mismatch."; exit 1; }
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
sudo install -m 0644 "$key_file" /etc/apt/keyrings/framebridge.asc
|
||||
printf '%s\\n' "deb [signed-by=/etc/apt/keyrings/framebridge.asc] '''+META['apt_base']+''' trixie main" | sudo tee /etc/apt/sources.list.d/framebridge.list >/dev/null
|
||||
sudo apt update
|
||||
sudo apt install '''+META['package_name']+'''
|
||||
)'''
|
||||
apt='<h2>Install from the signed APT repository</h2><p>This project repository distributes FrameBridge; it is not a mirror of the full Debian archive. The signing key below is scoped to this repository with <code>Signed-By</code>.</p>'+code(apt_commands)+'<p class="aside-note">The fingerprint pins the key expected by this release page. Obtain it through this HTTPS site or another trusted project channel. Do not bypass APT signature errors.</p>'
|
||||
download='<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Download / Verify / Install</p><h1>Get FrameBridge.</h1><p class="lead">Official packages and corresponding source, with a clear chain from the release page to the file you install.</p></div></div><div class="wrap page-content download-grid"><div class="prose">'+notice+manual+apt+'<h2>After installation</h2><p>Run the normal connection flow in a terminal on the computer you are connecting:</p>'+code('fb connect\nfb status')+'<p>Use your own account. Complete browser consent normally and enter credentials only at the hidden terminal prompts. Keep secrets out of chats, issue reports and copied command arguments.</p><p>For diagnosis, use <code>fb doctor</code>. See the <a href="/docs/">versioned command guide</a> for advanced roles and operation details.</p></div><aside>'+facts+'<p class="aside-note">A signature identifies an official release. It does not prevent you from exercising the modification and redistribution rights provided by the GPL.</p><p class="aside-note">Keep independently modified builds clearly distinguishable. Only reviewed releases published through the project’s official channels should be described as official.</p><a class="text-link" href="'+E(META['git_url'])+'">Browse the source →</a></aside></div></main>'
|
||||
download=CAT['download'](META)
|
||||
(DIST/'download/index.html').write_text(page('Download and verify',download,'download'))
|
||||
|
||||
community='''<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Community / Contribution</p><h1>Make the bridge better.</h1><p class="lead">Useful bug reports, small patches and clear explanations are a good place to begin.</p></div></div><div class="wrap page-content"><article class="prose"><h2>Start with the existing behavior</h2><p>Read the versioned guide, check the installed version and look for an existing issue. A reproducible report should name the command, expected result, actual result and relevant environment.</p><p><strong>Remove credentials and personal data from logs before sharing them.</strong> Never include passwords, access keys, private keys, tokens or cookies.</p><h2>Propose a focused change</h2><p>Use an issue to explain the problem or a pull request to propose a patch. Include the reason for the change and proportionate validation. Keep unrelated edits separate so they can be reviewed.</p>'''+('<h3>Clone the project</h3>'+code('git clone '+META['git_clone_url']) if META.get('git_clone_url') else '')+'''<p><a class="button primary" href="'''+E(META['git_url'])+'''">Open the project in Git</a></p><h2>Official releases and free forks</h2><p>Maintainers review which changes become official FrameBridge releases. Published release checksums and signatures identify those artifacts.</p><p>GNU GPLv3 also allows private modifications and independently distributed forks, subject to its terms. Contributing changes upstream is welcome; it is not a condition for exercising those rights. Modified builds should be clearly marked and must not be presented as official project releases.</p><p><a href="https://www.gnu.org/licenses/gpl-3.0.html" rel="noreferrer">Read the GNU GPLv3 license.</a> Third-party components retain their existing notices and licenses.</p><h2>Using an agent to contribute</h2><p>The command interface and MCP guide are designed for both people and authorized agents. Normal operation uses installed commands and tools. Editing or rebuilding the application is a development task that should follow the owner’s instructions.</p><h2>Project credits</h2><p><strong>Programming:</strong> ChatGPT<br><strong>Original concept and project coordination:</strong> Gerardo Betancourt</p><p>FrameBridge is an independent project. The credit describes how the project was developed and does not imply endorsement by OpenAI.</p></article></div></main>'''
|
||||
@@ -108,7 +64,7 @@ community='''<main id="content"><div class="page-header"><div class="wrap"><p cl
|
||||
|
||||
post='''<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Development journal / 26 October 2026</p><h1>Opening FrameBridge.</h1><p class="lead">A shared interface for people and agents, and a small invitation to improve it together.</p></div></div><div class="wrap page-content"><article class="prose"><p>FrameBridge began with a practical goal: use a conversation with ChatGPT to do useful work on Linux computers we control. Commands, persistent terminals and a compatible desktop should fit into the same working context.</p><h2>Make the interface easier to understand</h2><p>The current development work brings everyday commands into a shorter vocabulary, keeps status observation separate from diagnosis, and explains the effects of connection and startup controls. The long command <code>framebridge</code> and its short alias <code>fb</code> use the same interface.</p><p>The agent also needs a reliable starting point. A versioned guide and a brief MCP invitation are intended to help it discover how the installed software works, which computer receives an operation and what a command will change.</p><h2>Keep the evidence honest</h2><p>The preview video is a conceptual animation, not a recording of a live session. Real demonstrations and captures must remain distinguishable from illustrations. Independent tasks have been used together in the laboratory, but that experience does not establish automatic isolation or exclusive desktop control.</p><h2>Open source, with identifiable releases</h2><p>The project is being prepared for GNU GPLv3 distribution. Sources and package artifacts should be published together, with signatures and checksums identifying official releases. Community members remain free to adapt and redistribute the software under the license.</p><p>The first contribution can be small: a clearer explanation, a reproducible issue or a focused fix. Review gives those changes a path into a later official version.</p><p><a class="text-link" href="/community/">Read the contribution guide →</a></p></article></div></main>'''
|
||||
(DIST/'blog/2026-10-26-opening-framebridge/index.html').write_text(page('Opening FrameBridge',post,'blog'))
|
||||
support='''<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Support FrameBridge</p><h1>Help keep the bridge working.</h1><p class="lead">Voluntary support can help sustain a free software project: its hosting, maintenance, documentation and development.</p></div></div><div class="wrap page-content"><article class="prose"><h2>What support can help with</h2><ul><li><strong>Hosting and distribution:</strong> keeping the source code, documentation and release files available.</li><li><strong>Maintenance:</strong> investigating reported problems and keeping the software usable.</li><li><strong>Documentation:</strong> improving clear instructions for people and agents.</li><li><strong>Development:</strong> building and reviewing focused improvements.</li></ul><h2>Funding channel</h2><p>Funding details will be published here when a channel is available.</p><p>Contributions are voluntary. FrameBridge remains free software under GNU GPLv3 only, whether or not you provide financial support.</p><h2>Support without a payment</h2><p>A useful bug report, a clearer explanation or a carefully scoped patch can be valuable contributions. You can also help by sharing honest demonstrations that distinguish real recordings from conceptual illustrations.</p><p><a class="text-link" href="/community/">Read the contribution guide →</a></p><h2>Review stays independent</h2><p>Financial support does not grant control over official releases, bypass code review or security checks, or imply a project endorsement. Official changes follow the same review and release process.</p><p><a class="text-link" href="'''+E(META['git_url'])+'''">Explore the source code →</a></p></article></div></main>'''
|
||||
support='''<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Support FrameBridge</p><h1>Help keep the bridge working.</h1><p class="lead">Voluntary support can help sustain a free software project: its hosting, maintenance, documentation and development.</p></div></div><div class="wrap page-content"><article class="prose"><h2>What support can help with</h2><ul><li><strong>Hosting and distribution:</strong> keeping the source code, documentation and release files available.</li><li><strong>Maintenance:</strong> investigating reported problems and keeping the software usable.</li><li><strong>Documentation:</strong> improving clear instructions for people and agents.</li><li><strong>Development:</strong> building and reviewing focused improvements.</li></ul><h2>Voluntary support and contact</h2><p><a class="button primary" href="https://www.paypal.com/donate/?business=gerardobh1128%40gmail.com&currency_code=USD" rel="noreferrer">Support through PayPal</a></p><p>Project contact and PayPal account: <a href="mailto:gerardobh1128@gmail.com">gerardobh1128@gmail.com</a>.</p><p>Contributions are voluntary. FrameBridge remains free software under GNU GPLv3 only, whether or not you provide financial support.</p><h2>Support without a payment</h2><p>A useful bug report, a clearer explanation or a carefully scoped patch can be valuable contributions. You can also help by sharing honest demonstrations that distinguish real recordings from conceptual illustrations.</p><p><a class="text-link" href="/community/">Read the contribution guide →</a></p><h2>Review stays independent</h2><p>Financial support does not grant control over official releases, bypass code review or security checks, or imply a project endorsement. Official changes follow the same review and release process.</p><p><a class="text-link" href="'''+E(META['git_url'])+'''">Explore the source code →</a></p></article></div></main>'''
|
||||
(DIST/'support/index.html').write_text(page('Support FrameBridge',support,'support'))
|
||||
|
||||
blog='''<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Development journal</p><h1>Notes from the bridge.</h1><p class="lead">Design decisions, release work and what we learn while using FrameBridge.</p></div></div><div class="wrap page-content narrow"><a class="news-link" href="/blog/2026-10-26-opening-framebridge/"><time datetime="2026-10-26">26 October 2026 · Project notes</time><strong>Opening FrameBridge: a shared interface for people and agents.</strong><span>Read the development note →</span></a></div></main>'''
|
||||
@@ -157,4 +113,4 @@ hub+='</article>'
|
||||
(DIST/'docs/index.html').write_text(page('Command guide','<main id="content" class="wrap docs-layout">'+docs_nav('index')+hub+'</main>','docs',True))
|
||||
(DIST/'404.html').write_text(page('Page not found','<main id="content" class="wrap page-content prose"><p class="section-label">404 / Page not found</p><h1>This path ends here.</h1><p>Return to <a href="/">FrameBridge</a> or open the <a href="/docs/">command guide</a>.</p></main>'))
|
||||
(DIST/'robots.txt').write_text('User-agent: *\n'+('Allow: /\n' if READY else 'Disallow: /\n'))
|
||||
print(json.dumps({'web_root':str(DIST),'release_state':META['state'],'pages':len(list(DIST.rglob('*.html'))),'guide_topics':len(guides),'video_present':(DIST/'assets/framebridge-preview.mp4').is_file(),'ready_metadata_validated':READY},indent=2))
|
||||
print(json.dumps({'web_root':str(DIST),'release_state':META['state'],'pages':len(list(DIST.rglob('*.html'))),'guide_topics':len(guides),'video_present':(DIST/'assets'/VIDEO).is_file(),'ready_metadata_validated':READY},indent=2))
|
||||
+21
-11
@@ -51,7 +51,7 @@ def main():
|
||||
if not u.path.startswith('/'):failures.append('Non-root relative URL '+value);continue
|
||||
target=resolve(u.path)
|
||||
if not target.exists():
|
||||
if u.path=='/assets/framebridge-preview.mp4':pending.append('Preview video asset pending')
|
||||
if u.path=='/assets/framebridge-preview-0.3.1-original-r4.mp4':pending.append('Preview video asset pending')
|
||||
else:failures.append('Missing local target '+value+' referenced by '+str(p.relative_to(DIST)))
|
||||
elif u.path in q.assets:http_paths.add(u.path)
|
||||
if u.fragment and target.is_file() and target.suffix=='.html':
|
||||
@@ -78,33 +78,43 @@ def main():
|
||||
with tempfile.TemporaryDirectory(prefix='framebridge-web-qa-') as td:
|
||||
temp=Path(td);web=temp/'web';web.mkdir()
|
||||
shutil.copyfile(BASE/'build_site.py',web/'build_site.py')
|
||||
shutil.copyfile(BASE/'release_catalog.py',web/'release_catalog.py')
|
||||
shutil.copytree(BASE/'src',web/'src')
|
||||
(web/'assets').mkdir()
|
||||
for p in (BASE/'assets').iterdir():
|
||||
if p.is_file() and p.suffix!='.mp4':shutil.copyfile(p,web/'assets'/p.name)
|
||||
fixture=dict(meta,state='ready',sha256='a'*64,signing_fingerprint='A'*40,size_bytes=1,source_archive_url='/downloads/SYNTHETIC-SOURCE.tar.gz')
|
||||
if p.is_file():shutil.copyfile(p,web/'assets'/p.name)
|
||||
fixture=json.loads(json.dumps(meta));fixture.update(state='ready',signing_fingerprint='2B1F01BC806C0B33CB6CEDDE52859CEBC91B8808',checksums_url='/downloads/TEST-SHA256SUMS',checksums_signature_url='/downloads/TEST-SHA256SUMS.asc')
|
||||
fixture['source']=dict(filename='TEST-SOURCE.tar.gz',url='/downloads/TEST-SOURCE.tar.gz',signature_url='/downloads/TEST-SOURCE.tar.gz.asc',sha256='a'*64,size_bytes=1)
|
||||
for i,profile in enumerate(fixture['profiles']):
|
||||
filename='TEST-profile-'+str(i)+'.deb'
|
||||
profile.update(filename=filename,url='/downloads/'+filename,signature_url='/downloads/'+filename+'.asc',sha256='a'*64,size_bytes=1)
|
||||
for dependency in fixture['dependency_sources']:
|
||||
dependency.setdefault('signature_url',dependency['url']+'.asc')
|
||||
(web/'release.json').write_text(json.dumps(fixture))
|
||||
result=subprocess.run(['/usr/bin/python3','-I','-S',str(web/'build_site.py')],capture_output=True,text=True,timeout=10)
|
||||
if result.returncode:failures.append('Synthetic-ready rendering failed: '+result.stderr[-600:])
|
||||
else:
|
||||
q=Inspect();q.feed((web/'dist/download/index.html').read_text())
|
||||
blocks=[x for x in q.code if '\n' in x]
|
||||
if len(blocks)!=3:failures.append('Expected manual verification, APT setup and after-install command blocks')
|
||||
for text in blocks:
|
||||
result=subprocess.run(['/bin/bash','-n'],input=text,text=True,capture_output=True,timeout=3)
|
||||
if result.returncode:failures.append('Rendered install block shell syntax: '+result.stderr.strip())
|
||||
manual=next((x for x in blocks if 'gpgv --keyring' in x),'')
|
||||
if not manual or 'sha256sum --check' not in manual or 'expected="' not in manual:failures.append('Manual flow missing required verification')
|
||||
apt=next((x for x in blocks if 'sources.list.d' in x),'')
|
||||
if 'signed-by=' not in apt or 'apt-key' in apt:failures.append('APT trust not scoped with Signed-By')
|
||||
status={'host':'GERAMEG','website_source':str(BASE),'web_root':str(DIST),'language':'English','buildless':True,'html_pages_checked':len(pages),'loopback_http_paths_checked':http_count,'static_failures':failures,'pending_assets':sorted(set(pending)),'infra_routes_expected':sorted(infra),'external_links':sorted(external),'no_external_runtime_dependencies':True,'copy_controls_progressive':True,'ready_branch_rendered_with_synthetic_fixture':True,'install_blocks_syntax_checked_not_executed':True,'release_state':meta.get('state'),'package_sha256_present':bool(meta.get('sha256')),'signing_fingerprint_present':bool(meta.get('signing_fingerprint')),'video_present':(DIST/'assets/framebridge-preview.mp4').is_file(),'visual_browser_QA_performed':False,'visual_browser_QA_limit':'No browser or user desktop was operated by this subtask. Root may inspect the staged site via the authorized GERAMEG surface.','production_network_or_services_changed':False,'site_ready_for_private_staging':not failures,'public_release_ready':not failures and not pending and meta.get('state')=='ready','homepage_sha256':hashlib.sha256((DIST/'index.html').read_bytes()).hexdigest()}
|
||||
apt_blocks=[x for x in blocks if 'sources.list.d/framebridge.sources' in x]
|
||||
if len(apt_blocks)!=3:failures.append('Expected three independent APT profile setup blocks')
|
||||
for apt in apt_blocks:
|
||||
if 'Signed-By:' not in apt or 'primary_count' not in apt or 'apt-key' in apt:
|
||||
failures.append('APT key trust must be pinned and scoped with Signed-By')
|
||||
for suite in ['trixie','noble','mint22']:
|
||||
if not any('Suites: '+suite in block for block in apt_blocks):
|
||||
failures.append('Missing APT suite '+suite)
|
||||
status={'host':'GERAMEG','website_source':str(BASE),'web_root':str(DIST),'language':'English','buildless':True,'html_pages_checked':len(pages),'loopback_http_paths_checked':http_count,'static_failures':failures,'pending_assets':sorted(set(pending)),'infra_routes_expected':sorted(infra),'external_links':sorted(external),'no_external_runtime_dependencies':True,'copy_controls_progressive':True,'ready_branch_rendered_with_synthetic_fixture':True,'install_blocks_syntax_checked_not_executed':True,'release_state':meta.get('state'),'package_sha256_present':all(p.get('sha256') for p in meta.get('profiles',[])),'signing_fingerprint_present':bool(meta.get('signing_fingerprint')),'video_present':(DIST/'assets/framebridge-preview-0.3.1-original-r4.mp4').is_file(),'visual_browser_QA_performed':False,'visual_browser_QA_limit':'No browser or user desktop was operated by this subtask. Root may inspect the staged site via the authorized GERAMEG surface.','production_network_or_services_changed':False,'site_ready_for_private_staging':not failures,'public_release_ready':not failures and not pending and meta.get('state')=='ready','homepage_sha256':hashlib.sha256((DIST/'index.html').read_bytes()).hexdigest()}
|
||||
visual=BASE/'qa/VISUAL-REVIEW.json'
|
||||
if visual.exists():
|
||||
status['visual_browser_QA_performed']=True
|
||||
status['visual_browser_QA_limit']=json.loads(visual.read_text())['limitations']
|
||||
status['visual_browser_QA_report']=str(visual)
|
||||
status.update(support_page='/support/',support_funding_channel_configured=bool(meta.get('support_payment_url')),support_payment_form_present='<form' in (DIST/'support/index.html').read_text(),license_identifier=meta.get('license'),dependency_source_archives=len(meta.get('dependency_sources',[])))
|
||||
video=DIST/'assets/framebridge-preview.mp4'
|
||||
status.update(support_page='/support/',support_funding_channel_configured='https://www.paypal.com/donate/' in (DIST/'support/index.html').read_text(),support_payment_form_present='<form' in (DIST/'support/index.html').read_text(),license_identifier=meta.get('license'),dependency_source_archives=len(meta.get('dependency_sources',[])))
|
||||
video=DIST/'assets/framebridge-preview-0.3.1-original-r4.mp4'
|
||||
if video.exists():
|
||||
status['video_sha256']=hashlib.sha256(video.read_bytes()).hexdigest()
|
||||
guide=json.loads((DIST/'assets/command-guide.json').read_text())
|
||||
|
||||
@@ -1,25 +1,6 @@
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"state": "preparing",
|
||||
"channel": "Community release",
|
||||
"release_date": "2026-10-26",
|
||||
"distribution": "Debian 13 (trixie)",
|
||||
"architecture": "amd64",
|
||||
"package_name": "framebridge-original-expanded",
|
||||
"package_filename": "FrameBridge-0.3.1.deb",
|
||||
"package_url": "/downloads/FrameBridge-0.3.1.deb",
|
||||
"package_signature_url": "/downloads/FrameBridge-0.3.1.deb.asc",
|
||||
"checksums_url": "/downloads/SHA256SUMS",
|
||||
"checksums_signature_url": "/downloads/SHA256SUMS.asc",
|
||||
"source_archive_url": "",
|
||||
"sha256": "",
|
||||
"size_bytes": null,
|
||||
"signing_key_url": "/debian/framebridge-archive-keyring.asc",
|
||||
"signing_fingerprint": "",
|
||||
"apt_base": "https://framebridge.betancourt.one/debian",
|
||||
"git_url": "/git/framebridge/framebridge",
|
||||
"git_clone_url": "https://framebridge.betancourt.one/git/framebridge/framebridge.git",
|
||||
"site_url": "https://framebridge.betancourt.one",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependency_sources": [
|
||||
{
|
||||
@@ -27,14 +8,67 @@
|
||||
"filename": "FrameBridge-0.3.1-Community-Preview1-dependency-sources.tar.gz",
|
||||
"url": "/downloads/FrameBridge-0.3.1-Community-Preview1-dependency-sources.tar.gz",
|
||||
"sha256": "64540a85bd65eac3f394d8c0a967cacbc00e112676d57d644dcd1bb691c1926a",
|
||||
"size_bytes": 82606998
|
||||
"size_bytes": 82606998,
|
||||
"signature_url": "/downloads/FrameBridge-0.3.1-Community-Preview1-dependency-sources.tar.gz.asc"
|
||||
},
|
||||
{
|
||||
"label": "Tunnel client dependency sources",
|
||||
"filename": "FrameBridge-0.3.1-tunnel-client-dependency-sources.tar.gz",
|
||||
"url": "/downloads/FrameBridge-0.3.1-tunnel-client-dependency-sources.tar.gz",
|
||||
"sha256": "6445d4f0e773aba833fe5c610fbae54102b92de2ce636438f769f21e890c2bd9",
|
||||
"size_bytes": 26941745
|
||||
"size_bytes": 26941745,
|
||||
"signature_url": "/downloads/FrameBridge-0.3.1-tunnel-client-dependency-sources.tar.gz.asc"
|
||||
}
|
||||
],
|
||||
"version": "0.3.3",
|
||||
"state": "preparing",
|
||||
"release_date": "2026-10-28",
|
||||
"distribution": "Debian 13 \u00b7 Ubuntu 24.04 \u00b7 Linux Mint 22.x",
|
||||
"architecture": "amd64",
|
||||
"site_url": "https://framebridge.betancourt.one",
|
||||
"git_url": "/git/gerardo/framebridge",
|
||||
"git_clone_url": "https://framebridge.betancourt.one/git/gerardo/framebridge.git",
|
||||
"signing_key_url": "/debian/framebridge-archive-keyring.asc",
|
||||
"signing_fingerprint": "",
|
||||
"checksums_url": "",
|
||||
"checksums_signature_url": "",
|
||||
"source": {},
|
||||
"profiles": [
|
||||
{
|
||||
"profile_id": "debian13-amd64-py313",
|
||||
"suite": "trixie",
|
||||
"label": "Debian 13",
|
||||
"python": "3.13",
|
||||
"architecture": "amd64",
|
||||
"filename": "",
|
||||
"url": "",
|
||||
"signature_url": "",
|
||||
"sha256": "",
|
||||
"size_bytes": null
|
||||
},
|
||||
{
|
||||
"profile_id": "ubuntu24.04-amd64-py312",
|
||||
"suite": "noble",
|
||||
"label": "Ubuntu 24.04",
|
||||
"python": "3.12",
|
||||
"architecture": "amd64",
|
||||
"filename": "",
|
||||
"url": "",
|
||||
"signature_url": "",
|
||||
"sha256": "",
|
||||
"size_bytes": null
|
||||
},
|
||||
{
|
||||
"profile_id": "mint22-amd64-py312",
|
||||
"suite": "mint22",
|
||||
"label": "Linux Mint 22.x",
|
||||
"python": "3.12",
|
||||
"architecture": "amd64",
|
||||
"filename": "",
|
||||
"url": "",
|
||||
"signature_url": "",
|
||||
"sha256": "",
|
||||
"size_bytes": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
"""Validated release catalog and download content for the original website."""
|
||||
import html
|
||||
import re
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
E = lambda value: html.escape(str(value), quote=True)
|
||||
FINGERPRINT = "2B1F01BC806C0B33CB6CEDDE52859CEBC91B8808"
|
||||
PROFILES = (
|
||||
("debian13-amd64-py313", "trixie", "Debian 13", "3.13"),
|
||||
("ubuntu24.04-amd64-py312", "noble", "Ubuntu 24.04", "3.12"),
|
||||
("mint22-amd64-py312", "mint22", "Linux Mint 22.x", "3.12"),
|
||||
)
|
||||
VIDEO = "framebridge-preview-0.3.1-original-r4.mp4"
|
||||
POSTER = "framebridge-preview-0.3.1-original-r4.jpg"
|
||||
VIDEO_SHA256 = "b2a8b0b302063edbe05d84fb6dadb7f303a6f0f1b6d79dc151287df4671adc5b"
|
||||
POSTER_SHA256 = "2e867d4b6a22f81ecbefa293a88799b9e3c93f02aa7d1090c7f0a0dbfe44dc9f"
|
||||
|
||||
def require(condition, message):
|
||||
if not condition:
|
||||
raise ValueError(message)
|
||||
|
||||
def project_url(value, prefix=None):
|
||||
require(isinstance(value, str), "Artifact URL must be text")
|
||||
url = urlsplit(value)
|
||||
require(not url.query and not url.fragment and not url.username and not url.password,
|
||||
"Artifact URL must not contain credentials, a query or a fragment")
|
||||
require((not url.scheme and not url.netloc and url.path.startswith("/") and not value.startswith("//"))
|
||||
or (url.scheme == "https" and url.netloc == "framebridge.betancourt.one"),
|
||||
"Only canonical HTTPS or root-relative project URLs are allowed")
|
||||
require(".." not in url.path.split("/"), "Artifact URL path traversal")
|
||||
require(re.fullmatch(r"/[A-Za-z0-9/._+~:-]*",url.path), "Unsafe artifact URL characters")
|
||||
if prefix:
|
||||
require(url.path.startswith(prefix), "Unexpected artifact route")
|
||||
return value
|
||||
|
||||
def asset(spec):
|
||||
require(isinstance(spec, dict), "Artifact metadata missing")
|
||||
require(re.fullmatch("[a-f0-9]{64}", spec.get("sha256", "")), "Exact artifact SHA-256 required")
|
||||
require(isinstance(spec.get("size_bytes"), int) and spec["size_bytes"] > 0, "Exact artifact size required")
|
||||
require(re.fullmatch("[A-Za-z0-9][A-Za-z0-9._+~-]*", spec.get("filename", "")), "Safe artifact filename required")
|
||||
project_url(spec.get("url", ""), "/downloads/")
|
||||
project_url(spec.get("signature_url", ""), "/downloads/")
|
||||
|
||||
def validate(meta, ready):
|
||||
require(re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", meta.get("version", "")), "Release version must be numeric")
|
||||
require(meta.get("state") in ("preparing", "ready"), "Unknown release state")
|
||||
require(meta.get("site_url") == "https://framebridge.betancourt.one", "Canonical site origin required")
|
||||
require(meta.get("package_name") == "framebridge-original-expanded", "Unexpected binary package")
|
||||
require(meta.get("git_url") == "/git/gerardo/framebridge", "Use existing canonical Gitea repository")
|
||||
require(meta.get("git_clone_url") == "https://framebridge.betancourt.one/git/gerardo/framebridge.git",
|
||||
"Use existing canonical Gitea clone URL")
|
||||
expected = {x[0]: x[1:] for x in PROFILES}
|
||||
profiles = meta.get("profiles", [])
|
||||
require(isinstance(profiles, list) and len(profiles) == 3, "Exactly three profiles required")
|
||||
require(len({p.get("profile_id") for p in profiles}) == 3, "Duplicate profile")
|
||||
for profile in profiles:
|
||||
identity = profile.get("profile_id")
|
||||
require(identity in expected, "Unexpected profile")
|
||||
suite, label, python = expected[identity]
|
||||
require((profile.get("suite"), profile.get("label"), profile.get("python"),
|
||||
profile.get("architecture")) == (suite, label, python, "amd64"),
|
||||
"Profile identity does not match its tested distribution")
|
||||
if ready:
|
||||
asset(profile)
|
||||
if ready:
|
||||
require(meta.get("signing_fingerprint") == FINGERPRINT, "Unexpected official signing fingerprint")
|
||||
asset(meta.get("source"))
|
||||
deps = meta.get("dependency_sources", [])
|
||||
require(isinstance(deps, list) and len(deps) >= 2, "Dependency corresponding-source metadata required")
|
||||
for dependency in deps:
|
||||
asset(dependency)
|
||||
for key, prefix in (("signing_key_url", "/debian/"), ("checksums_url", "/downloads/"),
|
||||
("checksums_signature_url", "/downloads/")):
|
||||
project_url(meta.get(key, ""), prefix)
|
||||
|
||||
def code(text):
|
||||
return '<div class="codebox"><pre><code>' + E(text) + '</code></pre></div>'
|
||||
|
||||
def apt_commands(meta, profile):
|
||||
require(meta.get("state") == "ready", "Do not offer installation commands for an unsigned preparation")
|
||||
validate(meta, True)
|
||||
key_url = meta["site_url"] + meta["signing_key_url"] if meta["signing_key_url"].startswith("/") else meta["signing_key_url"]
|
||||
return """sudo apt install ca-certificates curl gnupg
|
||||
(
|
||||
set -eu
|
||||
key_file="$(mktemp)"
|
||||
trap 'rm -f "$key_file"' EXIT
|
||||
curl -fsSL '""" + key_url + """' -o "$key_file"
|
||||
key_info="$(gpg --batch --show-keys --with-colons "$key_file" 2>/dev/null)"
|
||||
actual="$(printf '%s\\n' "$key_info" | awk -F: '$1=="fpr" {print $10; exit}')"
|
||||
primary_count="$(printf '%s\\n' "$key_info" | awk -F: '$1=="pub" {n++} END {print n+0}')"
|
||||
test "$primary_count" -eq 1
|
||||
test "$actual" = '""" + FINGERPRINT + """'
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
sudo install -m 0644 "$key_file" /etc/apt/keyrings/framebridge-archive-keyring.asc
|
||||
printf '%s\\n' \\
|
||||
'Types: deb' \\
|
||||
'URIs: https://framebridge.betancourt.one/debian/' \\
|
||||
'Suites: """ + profile["suite"] + """' \\
|
||||
'Components: main' \\
|
||||
'Architectures: amd64' \\
|
||||
'Signed-By: /etc/apt/keyrings/framebridge-archive-keyring.asc' \\
|
||||
| sudo tee /etc/apt/sources.list.d/framebridge.sources >/dev/null
|
||||
sudo apt update
|
||||
sudo apt install framebridge-original-expanded
|
||||
)"""
|
||||
|
||||
def download(meta):
|
||||
ready = meta["state"] == "ready"
|
||||
body = '<main id="content"><div class="page-header"><div class="wrap"><p class="section-label">Download / Verify / Install</p><h1>Choose your Linux package.</h1><p class="lead">One network administration tool. Three distribution profiles. Updates through signed APT repositories.</p></div></div><div class="wrap page-content"><article class="prose">'
|
||||
body += '<p>FrameBridge helps one administrator manage their own home Linux network: inventory, services, storage, diagnostics, commands and persistent terminals. Desktop access is an auxiliary tool.</p>'
|
||||
body += '<h2>Match the target computer</h2>' + code("cat /etc/os-release\ndpkg --print-architecture\npython3 --version")
|
||||
body += '<p>The supported profiles below use amd64 and the distribution system Python. Select one matching suite for each computer.</p>'
|
||||
if not ready:
|
||||
body += '<div class="release-notice" role="status"><strong>Release preparation.</strong> Version ' + E(meta["version"]) + ' is being prepared. Download links and installation commands appear after the exact artifacts and signatures are verified.</div>'
|
||||
body += '</article><div class="profiles">'
|
||||
for profile in meta["profiles"]:
|
||||
body += '<section class="profile-card" id="' + E(profile["profile_id"]) + '"><p class="section-label">amd64 · Python ' + E(profile["python"]) + '</p><h2>' + E(profile["label"]) + '</h2><p>APT suite: <code>' + E(profile["suite"]) + '</code></p>'
|
||||
if ready:
|
||||
body += '<p class="hash">SHA-256: ' + E(profile["sha256"]) + '</p><p class="micro">' + E(profile["filename"]) + ' · ' + format(profile["size_bytes"], ",") + ' bytes</p><div class="actions"><a class="button primary" href="' + E(profile["url"]) + '" download>Download .deb</a><a class="button" href="' + E(profile["signature_url"]) + '">Package signature</a></div><details><summary>Install the signed APT channel</summary>' + code(apt_commands(meta, profile)) + '</details>'
|
||||
else:
|
||||
body += '<p class="micro">Version ' + E(meta["version"]) + ' · package and signature pending.</p>'
|
||||
body += '</section>'
|
||||
body += '</div><article class="prose">'
|
||||
if ready:
|
||||
body += '<h2>Release verification and source</h2><p>Expected signing-key fingerprint:</p><p class="hash">' + FINGERPRINT + '</p><p><a href="' + E(meta["checksums_url"]) + '">SHA256SUMS</a> · <a href="' + E(meta["checksums_signature_url"]) + '">Checksum signature</a> · <a href="' + E(meta["signing_key_url"]) + '">Public key</a></p><p><a href="' + E(meta["source"]["url"]) + '">Corresponding FrameBridge source</a> · <a href="' + E(meta["source"]["signature_url"]) + '">Source signature</a></p><p class="hash">SHA-256: ' + E(meta["source"]["sha256"]) + '</p><ul>'
|
||||
for dep in meta["dependency_sources"]:
|
||||
body += '<li><a href="' + E(dep["url"]) + '">' + E(dep.get("label", dep["filename"])) + '</a> · <a href="' + E(dep["signature_url"]) + '">Signature</a><br><span class="hash">SHA-256: ' + E(dep["sha256"]) + '</span></li>'
|
||||
body += '</ul><p>Third-party components retain their original licenses and notices. Unchanged dependency archives retain their historical filenames.</p><h2>Normal updates</h2>' + code("sudo apt update\napt-cache policy framebridge-original-expanded\nsudo apt install --only-upgrade framebridge-original-expanded\nfb status")
|
||||
body += '<p>APT verifies the repository signature and package hashes. Keep the selected distribution suite and its scoped Signed-By key.</p>'
|
||||
body += '<h2>Connect and work</h2><p>After installation, connect from the normal terminal on that computer:</p>' + code("fb connect\nfb status\nfb console") + '<p>One central installation can connect enrolled Linux nodes. Nodes use the central connection. Enter passwords and connection keys only in the normal local prompts.</p><p><a href="/docs/">Read the shared command guide</a> · <a href="' + E(meta["git_url"]) + '">Browse the canonical source repository</a></p></article></div></main>'
|
||||
return body
|
||||
@@ -7,3 +7,5 @@
|
||||
.docs{color-scheme:light;--bg:#f3f3ec;--panel:#e9ede5;--ink:#1c3024;--muted:#526357;--line:#cbd5c8;--accent:#795118;--mint:#3c7153}.docs .site-header{background:#19251e;color:#f3f3ec;border-color:#33443b}.docs .brand .prompt,.docs .cursor{color:#eac27b}.docs .site-header nav a{color:#c3cfc3}.docs .site-header nav a[aria-current=page]{color:#fff}.docs .nav-download{border-color:#566b5b}.docs-layout{display:grid;grid-template-columns:230px minmax(0,1fr);gap:65px;padding:50px 0 85px}.docs-nav{border-right:1px solid var(--line);padding-right:28px;font-size:13px}.docs-nav p{font:11px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}.docs-nav ul{list-style:none;padding:0;margin:15px 0 28px}.docs-nav a{display:block;text-decoration:none;padding:7px 0;color:var(--muted)}.docs-nav a[aria-current=page]{color:var(--ink);font-weight:650}.docs article{max-width:780px}.docs h1{font-size:46px;max-width:none}.docs .prose p,.docs .prose li{color:var(--muted)}.docs .codebox{background:#e7ece3}.docs .copy-button{background:#f3f3ec}.docs .section-label{margin-bottom:15px}.docs .site-footer{background:#e9ede5}.docs .credits strong{color:#1c3024}.topic-links{display:grid;grid-template-columns:1fr 1fr;gap:0 35px;margin:30px 0}.topic-link{display:block;padding:18px 0;border-top:1px solid var(--line);text-decoration:none}.topic-link strong{display:block;margin-bottom:6px}.topic-link span{font-size:13px;color:var(--muted)}
|
||||
@keyframes blink{50%{opacity:0}}@media(max-width:1000px){.hero{gap:45px}.header-inner{min-height:90px}nav{gap:17px}.brand{font-size:21px}.feature-row{grid-template-columns:170px 1fr 1fr;gap:25px}.release-panel{gap:35px}.architecture-line{font-size:12px;gap:10px}.docs-layout{grid-template-columns:190px minmax(0,1fr);gap:35px}}
|
||||
@media(max-width:760px){.wrap{width:calc(100% - 40px)}body{font-size:16px}.header-inner{align-items:flex-start;flex-direction:column;gap:14px;padding:21px 0 17px}.brand{font-size:22px}nav{gap:14px;width:100%;font-size:12px;overflow-x:auto;padding:3px 0 7px}.nav-download{padding:5px 10px}.hero{display:block;padding:50px 0 48px}.hero h1{font-size:57px;max-width:11ch}.hero .lead{font-size:18px}.terminal{margin:45px 8px 0 0;box-shadow:8px 9px 0 #1b2821}.terminal-caption{text-align:left}.terminal-body{padding:25px}.strip{flex-wrap:wrap;gap:12px 20px;font-size:10px;padding:20px 0}.section{padding:50px 0}.section-head,.workflow,.community,.download-grid{display:block}.section-head{margin-bottom:25px}.section-head p{padding:0}.video-caption{display:block}.video-caption span{display:block;margin-top:5px}.workflow-list{margin-top:30px}.architecture{padding:25px}.architecture-line{flex-direction:column;align-items:flex-start;gap:10px}.architecture-line .arrow{transform:rotate(90deg);margin-left:6px}.feature-row{grid-template-columns:1fr;gap:12px;padding:24px 0}.feature-row code{font-size:11px}.release-panel{display:block;padding:28px}.release-panel h2{font-size:34px}.release-facts{margin-top:30px}.community>div+div{margin-top:35px}.footer-main,.footer-bottom{display:block}.footer-links{margin:25px 0}.footer-bottom p+p{margin-top:12px}.page-header{padding:40px 0 25px}.page-header h1{font-size:43px}.page-content{padding:30px 0 55px}.download-grid aside{margin-top:40px}.docs-layout{display:block;padding:30px 0 55px}.docs-nav{border-right:0;border-bottom:1px solid var(--line);padding:0 0 16px;margin-bottom:30px}.docs-nav ul{display:flex;flex-wrap:wrap;gap:3px 20px;margin:10px 0}.docs-nav a{padding:3px 0}.docs h1{font-size:37px}.topic-links{grid-template-columns:1fr}.codebox{padding:20px 16px}pre{font-size:12px}.codebox pre{padding-right:0;padding-top:20px}}@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}.cursor{animation:none}}
|
||||
|
||||
.profiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;margin:40px 0}.profile-card{min-width:0;border:1px solid var(--line,#31433b);padding:24px}.profile-card h2{font-size:1.6rem}.profile-card .actions{gap:10px}.profile-card details{margin-top:24px}.profile-card pre{font-size:.75rem;overflow:auto}@media(max-width:1000px){.profiles{grid-template-columns:1fr}.profile-card{padding:20px}}
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python3
|
||||
"""Catalog guards and rendered shell syntax; no signing or installation."""
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import runpy
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
catalog = runpy.run_path(str(BASE / "release_catalog.py"))
|
||||
|
||||
class CatalogTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.meta = json.loads((BASE / "release.example.json").read_text())
|
||||
|
||||
def ready(self):
|
||||
meta = copy.deepcopy(self.meta)
|
||||
meta.update(state="ready", signing_fingerprint=catalog["FINGERPRINT"],
|
||||
checksums_url="/downloads/TEST-SHA256SUMS",
|
||||
checksums_signature_url="/downloads/TEST-SHA256SUMS.asc")
|
||||
meta["source"] = dict(filename="TEST-source.tar.gz", url="/downloads/TEST-source.tar.gz",
|
||||
signature_url="/downloads/TEST-source.tar.gz.asc", sha256="a"*64, size_bytes=1)
|
||||
for index, profile in enumerate(meta["profiles"]):
|
||||
name = "TEST-profile-" + str(index) + ".deb"
|
||||
profile.update(filename=name, url="/downloads/"+name,
|
||||
signature_url="/downloads/"+name+".asc", sha256="a"*64, size_bytes=1)
|
||||
return meta
|
||||
|
||||
def test_preparation_has_no_install_or_download_claim(self):
|
||||
catalog["validate"](self.meta, False)
|
||||
text = catalog["download"](self.meta)
|
||||
self.assertIn("Release preparation", text)
|
||||
self.assertNotIn("sudo apt install", text)
|
||||
self.assertNotIn("Download .deb", text)
|
||||
|
||||
def test_three_profile_shell_commands_are_valid_and_key_scoped(self):
|
||||
meta = self.ready()
|
||||
catalog["validate"](meta, True)
|
||||
for profile in meta["profiles"]:
|
||||
commands = catalog["apt_commands"](meta, profile)
|
||||
result = subprocess.run(["bash", "-n"], input=commands, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("Suites: " + profile["suite"], commands)
|
||||
self.assertIn("Signed-By:", commands)
|
||||
self.assertIn("primary_count", commands)
|
||||
self.assertNotIn("trusted=yes", commands)
|
||||
self.assertNotIn("apt-key", commands)
|
||||
|
||||
def test_wrong_distribution_metadata_is_rejected(self):
|
||||
self.meta["profiles"][1]["label"] = "Ubuntu 26.04"
|
||||
with self.assertRaisesRegex(ValueError, "Profile identity"):
|
||||
catalog["validate"](self.meta, False)
|
||||
|
||||
def test_duplicate_profiles_are_rejected(self):
|
||||
self.meta["profiles"][1] = self.meta["profiles"][0]
|
||||
with self.assertRaisesRegex(ValueError, "Duplicate profile"):
|
||||
catalog["validate"](self.meta, False)
|
||||
|
||||
def test_extra_shell_characters_in_key_url_are_rejected(self):
|
||||
meta = self.ready()
|
||||
meta["signing_key_url"] = "/debian/key';echo-malicious"
|
||||
with self.assertRaises(ValueError):
|
||||
catalog["validate"](meta, True)
|
||||
|
||||
def test_missing_artifact_checksum_is_rejected(self):
|
||||
meta = self.ready()
|
||||
meta["profiles"][2]["sha256"] = ""
|
||||
with self.assertRaisesRegex(ValueError, "SHA-256"):
|
||||
catalog["validate"](meta, True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in new issue
Block a user