OXYGENHUB
# b6dfcb61cf5e oxygenhub / templates / help.html
16.5 KiB · HTML · last changed Keys say when each was last used; Help explains AI identities (closes #286, closes #287) 15 days ago source blame history raw
43e8ca33 Andy McCall ↶ 1
{% extends "base.html" %}
2
{% block title %}Help{% endblock %}
3
{% block content %}
4
<div class="head"><h1>Help <small>using {{ shell.site }}</small></h1></div>
5
<div class="sectioned help">
6
<nav class="section-nav" aria-label="Sections">
7
  <a href="#start">Getting started</a>
8
  <a href="#identity">Your identity</a>
9
  <a href="#push">Pushing</a>
10
  <a href="#clone">Cloning</a>
11
  <a href="#proposals">Proposals</a>
12
  <a href="#delegate">Letting the hub act for you</a>
b349e15a Andy McCall ↶ 13
  <a href="#agents">AI identities</a>
43e8ca33 Andy McCall ↶ 14
  <a href="#flow">Pipelines</a>
15
  <a href="#keys">Keyboard</a>
d7d18246 Andy McCall ↶ 16
  <a href="#backups">Backups</a>
43e8ca33 Andy McCall ↶ 17
  <a href="#glossary">Coming from GitHub</a>
18
</nav>
19
<div class="sections">
20
21
<section class="panel" id="start">
22
  <h2>Getting started</h2>
23
  <p>{{ shell.site }} hosts <strong>Oxygen</strong> repositories. You work with them through <code>oxy</code>, the Oxygen client; the hub shows them, reviews them and runs their pipelines. Every command below is filled in for this hub.</p>
24
  <ol>
25
    <li>Install <code>oxy</code>: build it from the <code>oxygen</code> repository with <code>cargo build --release</code> and put <code>target/release/oxy</code> on your path.</li>
26
    <li>Say who you are and make a signing identity (next section).</li>
27
    <li>{% if shell.user.is_some() %}Create a <a href="/settings/tokens">personal access token</a>{% else %}<a href="/register">Register</a>, then create a personal access token under Settings › Tokens{% endif %} with <code>repo:write</code>. It is shown once; keep it in <code>oxy</code>'s configuration, not in scripts.</li>
28
    <li>{% if shell.user.is_some() %}<a href="/new">Create a repository</a>{% else %}Create a repository with <strong>+ NEW</strong>{% endif %} and push to it (below). An empty repository shows the same commands, with its own address.</li>
29
  </ol>
30
  <p class="small mute"><code>oxy doctor</code> checks your identity, configuration and remotes, and says what to do about any problem.</p>
31
</section>
32
33
<section class="panel" id="identity">
34
  <h2>Your identity</h2>
35
  <p>Oxygen changes are signed. Your identity is a key pair (Ed25519 with ML-DSA-65) kept under <code>~/.config/oxy</code>; the public part travels with your changes.</p>
36
  {% let cmd = "oxy config --user author.name \"Your Name\"\noxy config --user author.email [email protected]\noxy identity create\noxy config --user core.sign change" %}
37
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
38
  <p>Link the key to your account under {% if shell.user.is_some() %}<a href="/settings/keys">Settings › Keys</a>{% else %}Settings › Keys{% endif %}: the page gives you a challenge to sign with <code>oxy identity sign-challenge</code>, which proves you hold the key. Linked keys show on your profile, and the hub can then tell which signed changes are yours.</p>
39
</section>
40
41
<section class="panel" id="push">
42
  <h2>Pushing</h2>
43
  {% let ca = self.ca_setup() %}
44
  {% if !ca.is_empty() %}
45
  <p>This hub uses a self-signed certificate. Save it once, and point each remote at it:</p>
46
  <div class="cmds"><pre><code>{{ ca }}</code></pre><button class="small" data-copy="{{ ca }}">copy</button></div>
47
  {% endif %}
48
  <p>From a checkout, add the repository as a remote, give it your token, and push:</p>
49
  {% let cmd = q.connect(self.repo_url().as_str()) %}
50
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
51
  <p>An empty repository takes its identity from the first push, so every later clone shares it. <code>oxy push {{ q.remote }}</code> sends the current bookmark; <code>oxy push {{ q.remote }} --bookmark NAME</code> another one. Tags travel with pushes.</p>
52
  <p class="small mute">A Git history comes across with <code>oxy git import --branch main path/to/git-checkout NAME</code>: every commit becomes a change, and the result can be pushed like any other repository.</p>
53
</section>
54
55
<section class="panel" id="clone">
56
  <h2>Cloning</h2>
57
  {% let cmd = self.clone_cmd() %}
58
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
59
  <p>Public repositories clone without a token. For private ones, and to push back, set <code>oxy config remote.origin.token YOUR-TOKEN</code>{% if q.ca_url.is_some() %} and <code>oxy config remote.origin.ca {{ q.ca_file }}</code>{% endif %} in the clone. <code>oxy pull origin</code> brings in what others pushed; there is no merge commit, because Oxygen changes combine by themselves.</p>
60
</section>
61
62
<section class="panel" id="proposals">
63
  <h2>Proposals</h2>
64
  <p>A proposal asks for a bookmark's changes to be added to another bookmark, like a pull request. Push your bookmark, then open one from the repository's Proposals tab, from the compare page's <strong>Propose this</strong>, or from a fork.</p>
65
  <ul>
66
    <li>A proposal is always tested and reviewed on its <strong>projected state</strong>: exactly what the target will hold once it lands. It never goes stale, and there is nothing to rebase.</li>
67
    <li>Review comments are anchored to lines' atoms, so they stay on the right line through later pushes.</li>
68
    <li>An approval can be a <strong>seal</strong>: your signature over the proposal's changes (<code>oxy seal</code>), which anyone can check offline.</li>
69
    <li>Land from the page, or from a client with <code>oxy proposal land URL</code>, which signs the landing with your own key.</li>
7d2eb590 Andy McCall ↶ 70
    <li>Where the site, an organisation or a repository has set up <strong>AI assistants</strong> (Settings), you can ask one to review under Reviewers, or have it draft the description; its answer is recorded as yours <em>via</em> the assistant, with an <span class="tag">agent</span> badge. Only people approve.</li>
43e8ca33 Andy McCall ↶ 71
  </ul>
72
</section>
66fd9d6b Andy McCall ↶ 73
<section class="panel" id="wiki">
74
  <h2>Wiki</h2>
75
  <p>A repository's wiki is Markdown pages kept as an Oxygen repository of their own: edit them under the <strong>Wiki</strong> tab (every edit is a signed change, so it needs the same delegation as editing code), or clone and push them as <code>oxy clone {{ shell.base_url }}/OWNER/NAME.wiki</code>. Pages are <code>Name.md</code> on <code>main</code>; <code>_Sidebar.md</code> is the sidebar; link between pages with <code>[Other](Other)</code>.</p>
76
</section>
43e8ca33 Andy McCall ↶ 77
78
<section class="panel" id="delegate">
79
  <h2>Letting the hub act for you</h2>
80
  <p>Edits in the browser and landings from the proposal page are changes too, and they must be signed. The hub signs them as your <strong>delegate</strong>, only if you allow it. Allowing it is a one-off step:</p>
81
  {% let cmd = self.delegate_cmd() %}
82
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
b349e15a Andy McCall ↶ 83
  <p>That makes a new version of your identity: export it in the same checkout (<code>oxy identity export</code>) and upload that file under Settings › Keys, signing the challenge there too, so the hub sees the delegation. The hub's key file is not the one to upload. You can withdraw it at any time with <code>oxy identity delegate remove</code>; a repository's trust policy still decides whether a delegated change may update a bookmark.</p>
43e8ca33 Andy McCall ↶ 84
</section>
85
b349e15a Andy McCall ↶ 86
<section class="panel" id="agents">
87
  <h2>AI identities</h2>
88
  <p>An AI agent (a coding assistant, a bot) can work on your repositories in its own name and in yours: its changes are signed, they say which agent made them, and you decide what it may do and for how long. Nothing it is given is a password or a key of yours.</p>
89
  <h3>Keyless agents (recommended)</h3>
90
  <p>A keyless agent holds no key at all. You give it a <strong>grant</strong>: a token your own key signs, valid for a time you choose. Whenever the agent signs, it makes a fresh key, the hub certifies it for ten minutes under your grant, and the key is thrown away; a stolen home holds nothing that signs by itself.</p>
91
  <p>On your side, name the agent and how long it may act for you (the token is printed once):</p>
92
  {% let cmd = "oh agent grant claude --for 30d --scope changes,seal,refs" %}
93
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
94
  <p>Give the agent its own oxy home (never your own), and keep the token there; it is read from standard input, so it stays out of the shell's history:</p>
95
  {% let cmd = self.agent_home_cmd() %}
96
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
97
  <p>The agent then commits and pushes with <code>OXY_HOME</code> set to its home. Its changes are authored as you and sealed <code>via agent:claude@{{ who }}</code>, which history, proposals and your profile show. <code>oh agent list</code> shows your grants; <code>oh agent revoke ID</code> ends one at once, and a grant ends by itself when its time is up.</p>
98
  <h3>Where it may push</h3>
99
  <p>A repository with a trust policy accepts an agent's work only if the policy names it. Grant the agent's subject, with this hub as the issuer, from a checkout of the repository:</p>
100
  {% let cmd = self.subject_grant_cmd() %}
101
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
102
  <p>A repository without a policy takes any signed change its token may push.</p>
103
  <h3>Agents with a key of their own</h3>
104
  <p>An agent that keeps its own identity acts for you through a <strong>delegation</strong>: from your checkout, <code>oxy identity delegate add agent.identity --scope changes --expires 7d</code>, then link your identity again under {% if shell.user.is_some() %}<a href="/settings/keys">Settings › Keys</a>{% else %}Settings › Keys{% endif %}. <code>oxy identity delegate remove KEY</code> withdraws it. Keyless grants are simpler and leave nothing behind.</p>
105
  <h3>Tokens, notifications and gates</h3>
106
  <ul>
107
    <li>A personal access token an agent uses can be marked <strong>agent</strong> under {% if shell.user.is_some() %}<a href="/settings/tokens">Settings › Tokens</a>{% else %}Settings › Tokens{% endif %}: proposals it opens and comments it makes reach your inbox and email like anyone else's.</li>
108
    <li>An approval gate is for a person: an agent's token cannot approve or reject one unless a repository's maintainers allow it under Settings › Gates & agents, and each decision still says which agent made it.</li>
109
    <li>Commits whose message says a skip marker (<code>[skip flow]</code>) start no pipeline run; agents should leave them out unless asked.</li>
110
  </ul>
111
</section>
112
43e8ca33 Andy McCall ↶ 113
<section class="panel" id="flow">
114
  <h2>Pipelines</h2>
115
  <p>Pipelines run in <strong>OxygenFlow</strong>. Put a pipeline in <code>.flow/pipeline.kdl</code> and push: each job runs in its own microVM with no network, and its result shows on the repository's Flow tab and as a check named <code>flow/JOB</code> on proposals. A site administrator enables Flow for a repository.</p>
116
  <p>Instead of a badge, a README can carry a <strong>state stamp</strong>:</p>
117
  {% let cmd = "[![flow](.flow/stamp)](.flow/run)" %}
118
  <div class="cmds"><pre><code>{{ cmd }}</code></pre><button class="small" data-copy="{{ cmd }}">copy</button></div>
119
  <p>It shows the result for the state you are looking at (a tag shows the tag's result), names that state, and carries each job's receipt hash.</p>
120
</section>
121
122
<section class="panel" id="keys">
123
  <h2>Keyboard</h2>
124
  <table class="list">
125
    <tbody>
126
      <tr><td><kbd>/</kbd></td><td>Search</td></tr>
127
      <tr><td><kbd>g</kbd> <kbd>c</kbd></td><td>The repository's code</td></tr>
128
      <tr><td><kbd>g</kbd> <kbd>h</kbd></td><td>History of the bookmark you are on</td></tr>
129
      <tr><td><kbd>g</kbd> <kbd>p</kbd></td><td>Proposals</td></tr>
130
      <tr><td><kbd>g</kbd> <kbd>i</kbd></td><td>Issues</td></tr>
131
      <tr><td><kbd>g</kbd> <kbd>b</kbd></td><td>Bookmarks</td></tr>
132
      <tr><td><kbd>g</kbd> <kbd>t</kbd></td><td>Trust</td></tr>
133
      <tr><td><kbd>y</kbd></td><td>Replace the address with a permalink to this exact state (files and trees)</td></tr>
134
      <tr><td>click a line number, <kbd>Shift</kbd>-click another</td><td>Select lines; copy them or a permalink to them</td></tr>
135
      <tr><td><kbd>?</kbd></td><td>The keyboard sheet</td></tr>
136
    </tbody>
137
  </table>
138
</section>
139
d7d18246 Andy McCall ↶ 140
<section class="panel" id="backups">
141
  <h2>Backups</h2>
142
  <p>Under Settings › Backups (or an organisation's settings) you choose where your repositories are copied and how often. Each run copies every repository you own, its wiki, issues, proposals, releases and settings, but never a secret value. Only what changed travels, and every kept run can be restored with <code>oxygenhub admin backup restore</code>.</p>
143
  <p>A Google Cloud Storage bucket needs no key: the hub signs in as <code>backup:YOU</code> with a token it issues itself, which Google trusts through workload identity federation. Once per project, create a pool and an OIDC provider whose issuer is this hub's address; once per bucket, grant <code>roles/storage.objectAdmin</code> to the principal <code>…/subject/backup:YOU</code>. The exact commands are in the hub's <code>docs/backups.md</code> §6.</p>
144
</section>
145
43e8ca33 Andy McCall ↶ 146
<section class="panel" id="glossary">
147
  <h2>Coming from GitHub</h2>
148
  <table class="list glossary">
149
    <thead><tr><th>GitHub</th><th>Here</th><th>What is different</th></tr></thead>
150
    <tbody>
151
      <tr><td>commit</td><td><strong>change</strong></td><td>A change records edits, not a snapshot, and names the changes it depends on. Changes that do not touch the same lines combine in any order with the same result, so there are no merge commits. A change keeps its identity when it is rebased or squashed.</td></tr>
152
      <tr><td>commit SHA, tree</td><td><strong>state</strong></td><td>The files a set of changes makes. A state is addressed by its heads (<code>a1b2…+c3d4…</code>); checks, stamps and permalinks belong to states.</td></tr>
153
      <tr><td>branch</td><td><strong>bookmark</strong></td><td>A name for a state. It can have several <strong>heads</strong> (changes nothing else depends on); the tree is their union, not a conflict.</td></tr>
154
      <tr><td>tag</td><td><strong>tag</strong></td><td>An immutable named state, usually signed.</td></tr>
155
      <tr><td>merge, rebase</td><td><code>oxy merge</code>, <code>oxy pull</code></td><td>Uniting bookmarks adds the other side's changes. Nothing is rewritten and no merge change is made.</td></tr>
156
      <tr><td>merge conflict</td><td><strong>conflict</strong> in the state</td><td>A conflict is recorded in the history instead of blocking you. It stays visible (the Conflicts tab) until a change resolves it, and anyone can resolve it later.</td></tr>
157
      <tr><td>pull request</td><td><strong>proposal</strong></td><td>Reviewed and tested on its projected state. It cannot go stale, and approvals can be signed seals.</td></tr>
158
      <tr><td>branch protection</td><td><strong>trust policy</strong></td><td>Signed rules in the repository itself: who may move which bookmark, with how many signatures. Every client enforces them, not only the hub.</td></tr>
159
      <tr><td>Actions, CI badge</td><td><strong>OxygenFlow</strong>, state stamp</td><td>Jobs run in microVMs on exact states; results are receipts, and the stamp says which state they belong to.</td></tr>
c9aacc0e Andy McCall ↶ 160
      <tr><td>git submodule</td><td><strong>subrepo</strong></td><td>Another Oxygen repository pinned at a path by its heads, listed in <code>.oxymodules</code>. The pin is a change like any other, so bumping it combines with your other work instead of conflicting with it.</td></tr>
161
      <tr><td>Renovate, Dependabot</td><td><strong>Diffusion</strong></td><td>Built into the hub, not a bot you install. When a subrepo you pin moves on its default bookmark, the hub works out the bump, runs your checks on it and opens a proposal labelled <code>dependencies</code> with the upstream's changelog. Choose per repository under Settings › Diffusion (when the upstream is green, on every move, or off), or per subrepo in <code>.oxymodules</code>; it never lands anything for you. The Diffusion tab shows both directions: what you pin, and who pins you.</td></tr>
43e8ca33 Andy McCall ↶ 162
      <tr><td>git blame</td><td><strong>blame</strong></td><td>Exact through rebases and squashes: each line is an atom with one authoring change, so there is no "ignore revisions" file.</td></tr>
163
      <tr><td>fork</td><td><strong>fork</strong></td><td>A copy under your name with its own identity and bookmarks, sharing the stored objects. Its proposals go upstream as <code>you/name:bookmark</code>; the upstream's trust policy is not copied.</td></tr>
164
      <tr><td>personal access token</td><td><strong>token</strong></td><td>For pushing and the API. Your signing key is what proves authorship; a token only authorises.</td></tr>
165
    </tbody>
166
  </table>
167
</section>
168
169
</div>
170
</div>
171
{% endblock %}