# AI provenance in the GitLab archive

```{raw} html
<div class="chatbht-archive-brand" role="img" aria-label="chatBHT loves GitLab">
  <div class="chatbht-archive-brand__row">
    <img class="chatbht-archive-brand__chatbht" src="../_static/images/logo-neu.png" alt="chatBHT">
    <img class="chatbht-archive-brand__heart" src="../_static/images/archive-heart.svg" alt="loves">
    <img class="chatbht-archive-brand__gitlab" src="../_static/images/gitlab-logo.svg" alt="GitLab">
  </div>
</div>
```

**Archive** saves a completed chat state in GitLab. Press and hold the archive icon below a response for one second. It creates a versioned commit and a link to that exact state. In agent mode, the associated working files are archived too.

chatBHT and `gitlab.chatbht.com` use the same institutional sign-in. The additional GitLab authorisation allows chatBHT to use your selected repository as an archive.

## How the record is created

Select a chat state, archive it and include the commit link in your work. After the one-second hold, the archive contents are checked. Internal or public repositories require an additional sharing confirmation before a commit is created.

```{include} ../_includes/chatbht-archive-information-flow.md
```

```{admonition} Distinguish an AI-use record from a source
:class: important
An **academic citation** identifies the source of a factual statement. An **AI-use record** documents how generative AI contributed to the working process. They complement each other; neither replaces the other.
```

## Connect GitLab once

Archiving requires a repository on the connected GitLab instance. Configure it in the settings window. Afterwards, the reference action is available directly below AI responses.

### Step 1: Open settings

Open **Settings** using the gear icon in the header or **Account → Settings** in the sidebar. Select the **Archive** tab in the window.

```{include} ../_includes/ui-account.md
```

### Step 2: Connect GitLab

On the **Archive** tab, select **Connect GitLab**. If the integration has not been configured yet, the app indicates this here.

```{include} ../_includes/ui-gitlab-settings.md
```

Institutional sign-in and GitLab authorisation serve different purposes:

- **Single sign-on** ensures that chatBHT and GitLab use the same institutional identity.
- **GitLab authorisation** additionally permits chatBHT to perform the explicitly approved repository actions on that person's behalf.

If an institutional GitLab session is already open, sign-in may continue without entering credentials again. GitLab still displays the permissions to approve. The authorisation is personal, stored on the server and revocable.

After connecting successfully, you return to chatBHT. The archive section shows your GitLab username, **Connected** status and available archive destinations.

### Step 3: Select or create a repository

**Existing repository** and **Create new repository** appear at the top of **Archive**, before the connection status and destination list. Use an existing GitLab project or create a new repository directly from chatBHT.

```{include} ../_includes/ui-gitlab-repositories.md
```

#### Select an existing repository

1. Select **Existing repository**.
2. Choose a GitLab project you can write to.
3. Optionally specify a branch. If left blank, the default branch is used.
4. Set the target folder. The default is `chatbht`.
5. Confirm with **Use as archive destination**.

The project selector lists only projects for which the connected GitLab account has sufficient write permissions and an initialised repository.

```{include} ../_includes/ui-gitlab-picker.md
```

#### Create a new repository

1. Select **Create new repository**.
2. Enter a distinctive name, such as `ai-use-records`.
3. Choose its visibility. **Private (recommended)** is preselected.
4. Confirm with **Create and select**.

chatBHT creates the project through the GitLab API, initialises it and immediately sets it as the archive destination. Internal and public repositories require additional explicit confirmation: the complete chat and, in agent mode, the entire workspace become readable by signed-in institutional users or by everyone on the internet, respectively.

```{include} ../_includes/ui-gitlab-create.md
```

#### Set the archive destination

When settings are opened without an active chat context, a selected repository becomes the **Default archive destination**. When opened from a particular chat, an **Archive destination for this chat** can be set instead. Only repositories accessible to the connected GitLab user are shown; the HTML views use the example account **demo**.

The filled selection circle marks the active destination. **↗** opens the repository in GitLab; **×** removes it from the configured destinations. This does not delete the GitLab project itself.

#### Disconnect GitLab

Select **Disconnect** in the green connection panel to remove the connection. chatBHT attempts to revoke the GitLab authorisation and removes the locally stored connection. Existing commits and repositories remain in GitLab, subject to their access permissions.

## Archive a chat and copy its commit link

After setup, the archive action appears at the bottom right of each completed AI response, beside the Markdown/plaintext toggle and copy action. In agent mode, the associated worktree and generated files appear immediately below the response.

### Before archiving

The archive icon appears below the response. A commit link appears only after a successful archive. First review the complete chat and any working files.

```{include} ../_includes/ui-response.md
```

This preview is interactive: press and hold the archive icon to try the process with example data. It does not send data or create real commits.

### Hold for one second

As soon as you press the icon, a slim hint appears with **Hold**, a small ring and **1.0 s**. Keep holding while the ring fills clockwise and the countdown decreases in tenths of a second to **0.0 s**. On a phone, the indicator stays compact and clear of your finger.

```{include} ../_includes/ui-archive-hold.md
```

A short tap only shows the hint. Releasing early or moving your finger away cancels the action: the ring unwinds like a rotary dial and the countdown returns to **1.0 s**. The hint stays visible briefly; tapping elsewhere dismisses it too. Each new attempt requires a full second, so several short taps do not add up. With a keyboard, focus the icon and hold **Space** or **Enter**; **Escape** cancels. When reduced motion is enabled, the ring resets without an unwinding animation.

### Review institutional sharing

After the hold, chatBHT checks the destination and current contents. For a **private** repository, it archives the reviewed state directly. For **Internal**, it opens **Share within the institution?**.

```{include} ../_includes/ui-archive-release.md
```

The dialog identifies the repository, branch, message count, working files and audience. **Internal** means that signed-in institutional GitLab users can read the archived state according to GitLab access rules. This creates a GitLab snapshot; use [Share a chat](../sharing-collaboration/share-a-chat.md) separately to create a public chat link.

Also review any recorded personal context. Only then select the confirmation checkbox. This enables **Share and archive**:

```{include} ../_includes/ui-archive-release-approved.md
```

**Cancel** or **Escape** closes the dialog without creating a commit. For **Public**, the corresponding dialog explicitly states that anyone on the internet can read the contents. Holding the icon does not itself approve sharing.

### After the commit

After confirming, wait for the green checkmark and **Commit** link. Open it or copy its link address for your reference.

```{include} ../_includes/ui-archive-result.md
```

The link uses the full commit identifier rather than a changing branch reference. It therefore leads to the exact archive file created in that commit. Successful archiving remains visible on the response after reloading or reopening the chat.

Archiving saves the entire completed chat up to that point, including recorded personal context. In agent mode, the complete associated workspace is included in the same commit. Holding again checks the current state; identical contents do not create a duplicate commit.

## Chat history archive and GitLab evidence

There are two archive actions in the interface, and they answer different questions. In the chat list, open the three-dot menu and choose **Archive** to move a conversation out of the normal history view. Below a completed response, hold the **archive icon** to save the reviewed chat and, in agent mode, its worktree in GitLab.

| Action | What a reader gets |
| --- | --- |
| **Archive** in the chat list | An archived chat in the same chatBHT account. It does not create a GitLab commit or a public record. |
| **Archive icon** below a response | A GitLab commit containing the completed chat, recorded context, provenance and captured worktree. The commit link fixes the saved version. |

```{include} ../_includes/ui-chat-menu.md
```

A [shared chat link](../sharing-collaboration/share-a-chat.md) is created separately with **Share**. It lets the selected audience read a chat through chatBHT; it is not the GitLab commit. The example below shows both links, so readers can open the conversation in the app and independently inspect the saved GitLab evidence. Temporary chats cannot be transferred to GitLab.

## What is archived?

The readable Markdown transcript starts with a YAML header and lists messages in order. The same commit also contains a JSON provenance record and, in agent mode, a byte-for-byte copy of regular worktree files.

| Part of the saved state | What it helps a reader establish |
| --- | --- |
| **Chat ID, title and timestamps** | Which conversation was saved and when each message and the archive were recorded. |
| **Messages, roles and recorded context** | What was asked and answered; model routing and personalisation appear when they were recorded. |
| **Content SHA-256** | Which canonical chat and worktree manifest belong to this snapshot. |
| **Worktree manifest and files** | Names, sizes and SHA-256 checksums for the captured files, plus their original bytes. |
| **`chatBHT-provenance.json`** | Relationships between the recorded person, chat activity, model route, messages and file entities, together with capture details and explicit evidence limits. |

The default layout is:

```text
chatbht/chats/<chat-id>/<title>-<content-hash>.md
chatbht/chats/<chat-id>/<title>-<content-hash>/chatBHT-provenance.json
chatbht/chats/<chat-id>/<title>-<content-hash>/worktree/<file-path>
```

An empty agent directory is represented by a `.gitkeep` file. Git's internal metadata is not copied into the worktree snapshot. Symbolic links are not followed; finding one stops the archive rather than silently omitting a file. Identical content in the same project and branch is reused after verification; a changed chat or worktree produces a new version.

## Example: one agent run, one public GitLab commit

On **15 September 2026**, a user asked an agent to create a publication-ready graphical proof of `(a + b)² = a² + 2ab + b²`. The prompt requested exactly two deliverables: a short English `README.md` and an accessible standalone SVG. The archived transcript contains the prompt and one assistant response. The worktree contains both requested files; the commit also contains the provenance JSON and a `.gitkeep` for an empty `artifacts/` directory.

[Open the shared chat in chatBHT](https://chatbht.com/chat/shared/c457be1a-935d-44b0-86c6-58b83307d4ef/) to see the conversation in the app. This is a separate public view of the same titled chat; the GitLab commit below is the fixed archive evidence.

[Open the exact archive commit ba895503c93ff202a2d6d3c08568452aadec27d0](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/commit/ba895503c93ff202a2d6d3c08568452aadec27d0) in the public `open-weight-models-benchmark` repository. All links below use that commit identifier rather than `main`, so they continue to describe this snapshot if the branch changes.

| Evidence in that commit | Open | Download the archived bytes |
| --- | --- | --- |
| Chat transcript and YAML header | [Markdown](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523.md) | [Download Markdown](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/raw/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523.md) |
| Provenance record | [`chatBHT-provenance.json`](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/chatBHT-provenance.json) | [Download JSON](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/raw/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/chatBHT-provenance.json) |
| Agent's explanation | [`README.md`](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/worktree/README.md) | [Download README](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/raw/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/worktree/README.md) |
| Graphical proof | [`first-binomial-theorem.svg`](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/worktree/first-binomial-theorem.svg) | [Download SVG](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/raw/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/worktree/first-binomial-theorem.svg) |

The [worktree directory at this commit](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/tree/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/worktree) lets you inspect the two deliverables together. The SVG below is a local copy of the archived file, so the figure also loads from `docs.chatbht.com` when this documentation is published.

```{figure} ../../_static/images/first-binomial-theorem.svg
:alt: Area proof of the first binomial theorem with a square of side a plus b split into a squared, two a by b rectangles and b squared
:name: first-binomial-theorem-archive-example
:width: 720px

The SVG captured in the agent worktree: four labelled regions make the area identity visible. Its SHA-256 is `92cd8fa76e50fcd25a6013c95f7f09b3b27d2cc3ca90a98ae3f8fa860114fe33`.
```

### Read the archive metadata

The Markdown file begins with this header from the example. The model value is the chat's stored model name; the separate route below records the model and provider used for the assistant generation.

```yaml
schema: "chatbht.archive/v2"
chat_id: "27721406-bf85-47c7-8264-910844d79ec3"
title: "First Binomial Theorem - DeepSeek V4 Flash 0731"
archived_at: "2026-09-15T00:12:55.354889+00:00"
model: "deepseek_v4_flash"
message_count: 2
worktree_file_count: 2
worktree_total_size: 4528
content_sha256: "e4dd09c71523230a8c47c00c824531ecb2d4e1dba40b4e684805f996e41b92ff"
```

The 4,528 bytes are the combined sizes of the two captured worktree files, not the whole repository. The transcript also records `generation_routing` as agent mode, model `deepseek-v4-flash-0731`, provider `deepseek`; it records that memories were disabled and none were used. These fields describe what chatBHT captured for this run. This example contains no archived session log or token-usage entries, so the old example's token totals do not apply.

### The three P's: Prompt, Process, Provenance

The [provenance JSON in this same commit](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523/chatBHT-provenance.json) uses W3C PROV relationships. The **three P's** are a practical reading order for the archive, not literal JSON field names:

| P | What to inspect in this example |
| --- | --- |
| **Prompt** | Read the user message in the [archived transcript](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/blob/ba895503c93ff202a2d6d3c08568452aadec27d0/chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523.md). It asks for exactly `README.md` and `first-binomial-theorem.svg`, defines the area proof and requires inspection. The JSON also records the prompt as user message `7144`, its timestamp and its attribution to the connected person. |
| **Process** | Follow the recorded assistant generation after that prompt: message `7145`, agent mode, model `deepseek-v4-flash-0731` and provider `deepseek`. The answer and two worktree files show the saved outcome. The UI process then requires a one-second hold, review and, for this public repository, explicit release before the commit. The record does not contain every runtime input or an agent session log. |
| **Provenance** | Use the commit-fixed JSON and file links to establish which person selected the snapshot, which public project and branch received it, when it was archived and which bytes were captured. The JSON links the prompt, generation and file entities, gives checksums, and states where attribution or attestation is not recorded. |

Selected fields from the JSON show where the state was saved and how strong the attestation is:

```json
{
  "schema": "chatbht.provenance/v1",
  "capture": {
    "archived_at": "2026-09-15T00:12:55.354889+00:00",
    "branch": "main",
    "gitlab_origin": "https://gitlab.chatbht.com",
    "project_id": 20,
    "selected_by": "https://auth.chatbht.com/o/subjects/55",
    "visibility": "public",
    "markdown_path": "chatbht/chats/27721406-bf85-47c7-8264-910844d79ec3/first-binomial-theorem-deepseek-v4-flash-0731-e4dd09c71523.md",
    "markdown_sha256": "7d141f8b052c13c31a6a181df04a84d72123fb6b03db12a84d4fdfac7b79a157"
  },
  "attestation": {
    "signed": false,
    "trusted_timestamp": false
  }
}
```

Here `selected_by` is the recorded identity reference, `project_id` identifies the GitLab destination, and `markdown_path` locates the transcript within it. The JSON was written alongside the transcript and files in the commit; it does not itself contain the later GitLab commit SHA. The commit link supplies that fixed version.

The SVG's actual graph entry is especially useful when checking a downloaded file:

```json
{
  "@id": "./worktree/first-binomial-theorem.svg",
  "@type": "prov:Entity",
  "generation_attribution": "not_recorded",
  "sha256": "92cd8fa76e50fcd25a6013c95f7f09b3b27d2cc3ca90a98ae3f8fa860114fe33",
  "size": 3223
}
```

`generation_attribution: "not_recorded"` is a deliberate limit: the archive preserves the SVG bytes and the assistant's statement about its work, but it does not independently establish which individual model call or human edit produced the file. Likewise, the generation activity says `input_context_completeness: "not_recorded"`; it links the recorded prompt but does not claim to contain every runtime input. The provenance file is neither signed nor given a trusted timestamp. The short-lived signed review used before committing is a different mechanism.

The public destination requires a release confirmation in the UI, but this JSON does not contain a separate event log of that gesture. Its `capture.visibility` records the destination's visibility at capture time.

### Follow the fixed archive state

The GitLab commit identifier fixes a repository version. The `content_sha256` in the Markdown identifies the canonical chat content and worktree manifest used to name this snapshot; it is not the SVG's checksum. `capture.markdown_sha256` checks the transcript's exact bytes, while each worktree entity's SHA-256 checks its own file. A reader can open the pinned commit, read the transcript, inspect the provenance relationships, download the files and compare their checksums. The public repository makes these links readable without a GitLab account; the [shared chat](https://chatbht.com/chat/shared/c457be1a-935d-44b0-86c6-58b83307d4ef/) is a separate view of the conversation.


## Choose archive visibility

New repositories are private by default. Appropriate visibility depends on the working context and sensitivity of the contents.

| Visibility | Access and suitable use |
| --- | --- |
| **Private** | Access is limited to authorised project members. Suitable for personal working states, confidential projects and records before release. |
| **Internal** | Eligible authenticated users of the GitLab instance can view the contents under the instance's rules. Suitable for internal teaching and institutional review. |
| **Public** | The record may be accessible without signing in. Suitable for published work and teaching materials when privacy, copyright and confidentiality requirements allow it. |

GitLab supports these three visibility levels. Administrators can further restrict the available options. See [GitLab's project and group visibility documentation](https://docs.gitlab.com/user/public_access/).

A private commit link identifies a precise state, but outsiders need permission to read it. For a public publication, the record must be accessible to its intended readers. An internal repository requires an eligible account on the instance; it is not generally public.

```{warning}
Do not archive personal, confidential, copyrighted or otherwise sensitive contents in an internal or public repository without the necessary authorisation. Check visibility before publishing a record.
```

## Include the commit link in academic work

The required form still depends on the citation style, discipline and requirements of the course, examinations office or publisher. A commit link can supplement a disclosure with a verifiable interaction record.

### Example methodological disclosure

> An agent routed to **deepseek-v4-flash-0731** through chatBHT was asked to explain the first binomial theorem and produce an SVG. The [shared conversation](https://chatbht.com/chat/shared/c457be1a-935d-44b0-86c6-58b83307d4ef/) can be read in the app; the chat, worktree and provenance were saved in [GitLab commit ba895503c93ff202a2d6d3c08568452aadec27d0](https://gitlab.chatbht.com/gdyballa/open-weight-models-benchmark/-/commit/ba895503c93ff202a2d6d3c08568452aadec27d0). The SVG should be checked as a mathematical explanation before reuse.

## Limits of the record

A commit documents which chat and working state was archived. It does not establish that the statements it contains are correct. Responsibility for checking, selecting, revising and interpreting the material remains with the person using it.

In particular:

- AI output does not replace original academic sources.
- Literature mentioned by a model must be checked and cited directly.
- A commit documents a saved state, not the truth of its statements.
- Uploaded images and documents are not automatically included as original files in the current archive format.
- A link's availability depends on the visibility and continued existence of the GitLab project.
- The archived model name identifies the model stored for the chat; further technical details depend on the installation.
- Long-term preservation requirements may additionally call for protected branches, signed commits or an institutional repository.

## Technical background

The one-second hold is a browser interaction. Releasing early immediately cancels its timer; no archive request is sent while the ring unwinds either. The decimal countdown and progress ring use the same hold interaction in the app and in these offline previews.

After the hold, the server checks the signed-in account, repository write access, visibility and the contents of the chat and worktree. It returns a signed review valid for ten minutes. The commit request checks that the contents and destination still match that review; changes require another review. Internal and public destinations also require explicit confirmation.

The Markdown chat, worktree files and `chatBHT-provenance.json` are written together in one GitLab commit. A SHA-256 content hash identifies the state, and the archive tag records its association. The displayed link uses the full commit SHA, so it keeps pointing to that saved version as the branch changes. Matching existing archives are checked and reused. Repository access uses the user's revocable OAuth grant; institutional sign-in establishes identity separately.

## Next step

The next chapter shows how to start a chat in chatBHT.

[Next: Start a chat](start-a-chat.md)
