Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 40 additions & 38 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
name: Publish Docker Image

on:
push:
branches:
- main
push:
branches:
- main
pull_request:

env:
REGISTRY: ghcr.io
REGISTRY: ghcr.io

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}
tags: type=raw,value=latest

- name: Build and push image
uses: docker/build-push-action@v5
with:
push: true
context: .
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}
tags: type=raw,value=latest

- name: Build and push image
uses: docker/build-push-action@v5
with:
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
context: .
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ChronDB is a chronological key/value database implemented in Clojure and backed
- Handle repository creation and concurrency with JGit primitives
- Surface meaningful errors when Git operations fail (lock contention, missing refs)
- Avoid destructive operations; prefer new commits over in-place mutation
- Ensure every write path calls the shared transaction helpers so that Git notes capture `tx_id`, origin, user, flags, and protocol metadata. Treat missing notes as regressions.

### API Development

Expand Down
24 changes: 24 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,28 @@ All operations within the transaction block are atomic:
- Changes are only visible after successful commit
- Automatic rollback on failure

#### Transaction Metadata and Git Notes

ChronDB attaches a structured Git note to every commit. The payload includes the transaction id (`tx_id`), origin, optional user identifier, flags, and request metadata. You can enrich this payload by providing HTTP headers on REST requests:

| Header | Description |
| --- | --- |
| `X-ChronDB-Origin` | Overrides the origin label stored in the note (defaults to `rest`). |
| `X-ChronDB-User` | Associates commits with an authenticated user id or service account. |
| `X-ChronDB-Flags` | Comma-separated list of semantic flags (for example: `bulk-load, migration`). |
| `X-Request-Id` / `X-Correlation-Id` | Propagate request correlation identifiers into commit metadata. |

All ChronDB front-ends (REST, Redis, SQL) reuse the same transaction core, so commits that belong to the same logical operation share the same `tx_id`. Special operations automatically set semantic flags. For instance, document imports mark the transaction with `bulk-load`, and restore flows add `rollback`.

Inspect commit notes with standard Git tooling:

```
git log --show-notes=chrondb
git notes --ref=chrondb show <commit-hash>
```

You can parse the JSON payload to correlate commits with external systems or audit trails.

### Event Hooks

#### Register Hook
Expand Down Expand Up @@ -263,3 +285,5 @@ Example error handling:
(log/error "Storage error:" (.getMessage e)))
(catch chrondb.exceptions.ValidationException e
(log/error "Validation error:" (.getMessage e))))

```
19 changes: 19 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ ChronDB is built in layers:
4. Indices are updated to reflect changes
5. Reads can access any point in time using specific commits

### Transaction Metadata via Git Notes

Every commit recorded by ChronDB receives a Git note under the `chrondb` ref. The note payload is JSON and contains:

- A transaction identifier (`tx_id`) shared by all commits that belong to the same logical operation
- The origin (for example `rest`, `redis`, `sql`, `cli`)
- Optional user information and request correlation ids
- Semantic flags such as `bulk-load`, `rollback`, `migration`, or `automated-merge`
- Additional metadata supplied by the protocol handler (HTTP endpoint, Redis command, SQL table, etc.)

These notes provide an append-only audit trail without mutating commit messages or tracked files. Operators can inspect them using standard tooling:

```
git log --show-notes=chrondb
git notes --ref=chrondb show <commit>
```

Because they live outside the object graph, notes can be replicated, filtered, and queried independently of the document contents while preserving Git’s immutable history.

### Indexing Layer Details

The Lucene layer receives document mutations from the storage layer and updates the appropriate secondary indexes. It maintains statistics about term distributions and query plans so that complex requests—such as multi-field boolean filters or temporal slices—can be executed without scanning entire collections. When a query arrives, the planner determines the optimal combination of indexes, warms the cache when necessary, and streams results back to the access layer. Geospatial fields are stored in BKD trees, while full-text fields use analyzers that can be tuned per collection.
Expand Down
21 changes: 21 additions & 0 deletions docs/examples-clojure.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,27 @@ ChronDB supports atomic transactions:
;; If any operation fails, all changes are rolled back
```

You can customise the transaction metadata written to Git notes via the `chrondb.transaction.core` helpers:

```clojure
(require '[chrondb.transaction.core :as tx])

(tx/with-transaction [db {:origin "cli"
:user "admin"
:flags ["migration"]
:metadata {:request "seed-dataset"}}]
;; Flags can be appended dynamically as context evolves
(tx/add-flags! "bulk-load")
(tx/merge-metadata! {:batch-size 3})

(doseq [doc [{:id "product:1" :name "Laptop" :price 1200}
{:id "product:2" :name "Phone" :price 800}
{:id "product:3" :name "Tablet" :price 600}]]
(chrondb/save db (:id doc) doc)))

;; Inspect the resulting Git notes with: git log --show-notes=chrondb
```

## Advanced Features

### Custom Hooks
Expand Down
Loading
Loading