@colony2/gitvend 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +43 -28
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  A Go Git/HTTPS gateway with permissions carried in signed JWTs. Agents use ordinary Git and one gateway credential across their assigned repositories. The gateway holds a shared upstream token and enforces repository, ref-discovery, branch-write, tag and repository-creation permissions before forwarding requests.
4
4
 
5
- Implemented for GitHub and GitHub Enterprise-compatible APIs. Client and upstream Git connections use HTTPS; fetch/ref lookup requires protocol v2, while pushes use Git's normal receive-pack format. The service does not require a GitHub App.
5
+ Implemented for GitHub and GitHub Enterprise-compatible APIs. Client and upstream Git connections use HTTPS; fetch/ref lookup requires protocol v2, while pushes use Git's normal receive-pack format. The service does not require a GitHub App. The gateway is stateless: it needs configuration, public verification keys and upstream credentials, with no database or writable state volume. Multiple instances can serve the same agents.
6
6
 
7
7
  This guide takes an operator from configuring a gateway to giving an agent a credential, cloning a repository, and pushing an allowed branch.
8
8
 
9
- [Install](#install) · [Set up the gateway](#set-up-the-gateway) · [Issue a credential](#issue-an-agent-credential) · [Agent usage](#use-gitvend-as-an-agent) · [Permissions](#permission-strings) · [Operations](#provisioning-and-audit) · [Troubleshooting](#troubleshooting) · [CLI reference](#cli-reference)
9
+ [Install](#install) · [Docker Compose](docs/docker-compose.md) · [Set up the gateway](#set-up-the-gateway) · [Issue a credential](#issue-an-agent-credential) · [Agent usage](#use-gitvend-as-an-agent) · [Permissions](#permission-strings) · [Operations](#provisioning-and-audit) · [Troubleshooting](#troubleshooting) · [CLI reference](#cli-reference)
10
10
 
11
11
  ## How credentials and permissions fit together
12
12
 
@@ -42,6 +42,15 @@ gitvend version
42
42
 
43
43
  For a Go installation, add `$(go env GOPATH)/bin` to your `PATH` if needed. The npm wrapper requires Node 22+ and `tar`. Install scripts must be enabled so it can download the binary from GitHub Releases. Release builds report their release version; local builds report their Git revision when available.
44
44
 
45
+ ### Docker Compose
46
+
47
+ The [Compose deployment guide](docs/docker-compose.md) covers a container image,
48
+ automatic signing-key initialization on persistent volumes, a mounted GitHub
49
+ token, and on-demand JWT issuance. Only the gateway stays running; the private
50
+ signing key is available to initialization and issuer containers, never mounted
51
+ into the gateway. The default stack binds HTTP to host loopback for use behind
52
+ an HTTPS reverse proxy.
53
+
45
54
  ## Build and verify
46
55
 
47
56
  Requires Go 1.25+ and Git for the integration tests. Supported server platforms are Linux and macOS. Run these commands from the source checkout's root:
@@ -91,7 +100,7 @@ Keep `var/signing.key` on that machine. Copy **only** `signing.pub` to `var/sign
91
100
 
92
101
  ### 3. Configure and start the server
93
102
 
94
- On the gateway, create its writable state directory and copy the [configuration template](examples/gitvend.json) from the source checkout or release archive. If installed through Go or npm, obtain that template separately from this repository:
103
+ On the gateway, prepare the public-key directory and copy the [configuration template](examples/gitvend.json) from the source checkout or release archive. If installed through Go or npm, obtain that template separately from this repository:
95
104
 
96
105
  ```sh
97
106
  mkdir -p var
@@ -104,7 +113,6 @@ Edit `gitvend.json` using this checklist:
104
113
  | --- | --- |
105
114
  | `listen` | `0.0.0.0:8443` to accept connections on the gateway's network interfaces. The template binds to `127.0.0.1:8443` for local access. |
106
115
  | `tls_cert`, `tls_key` | Readable certificate/key files for `git-gateway.example`, trusted by the agents. |
107
- | `state_file` | A persistent, writable database path; default `var/gitvend.db`. |
108
116
  | `keys[].public_key_file` | The public key installed in step 2. |
109
117
  | `keys[].owners` and `providers[].allowed_owners` | Your lowercase organization names in both places. |
110
118
  | `providers[].credential.secret_ref` | `env:GITHUB_TOKEN` or the mounted token file reference. |
@@ -159,7 +167,7 @@ gitvend sign \
159
167
  -output var/agent-47.jwt
160
168
  ```
161
169
 
162
- Alternatively, pass `-grant examples/grant.json`. The file contains `v`, `permissions`, and optional `bindings` mapping canonical `host/owner/repository` names to string-valued provider repository IDs. `-output` replaces the token file atomically with permissions `0600`; without it, `sign` writes the token to stdout. `keygen` refuses to overwrite existing key files.
170
+ Alternatively, pass `-grant examples/grant.json`. The file contains `v` and the `permissions` array. Permissions follow repository names, including future repositories with matching names; there are no repository-ID bindings. `-output` replaces the token file atomically with permissions `0600`; without it, `sign` writes the token to stdout. `keygen` refuses to overwrite existing key files.
163
171
 
164
172
  JWTs use Ed25519 signatures (`alg=EdDSA`, `typ=gitvend+jwt`). Verification requires the configured issuer, key ID and audience plus subject, token ID, issued-at, not-before, expiry and grant version. Unknown fields, duplicate JSON keys, unsupported algorithms and malformed rules are rejected. The gateway needs no issued-token database and never contacts the issuer on a Git request.
165
173
 
@@ -275,7 +283,7 @@ gitvend explain \
275
283
  -action ref.update
276
284
  ```
277
285
 
278
- The example returns `"allowed": false`. `explain` evaluates only the rules you pass; include applicable deny rules yourself. It does not load server configuration, verify a JWT, or check GitHub permissions. A valid explanation command exits successfully even for a denied decision; inspect `allowed`. A `repo.create` explanation evaluates the `c` permission only, not the additional branch-overlap, quota or upstream checks.
286
+ The example returns `"allowed": false`. `explain` evaluates only the rules you pass; include applicable deny rules yourself. It does not load server configuration, verify a JWT, or check GitHub permissions. A valid explanation command exits successfully even for a denied decision; inspect `allowed`. A `repo.create` explanation evaluates the `c` permission only, not the additional branch-overlap or upstream checks.
279
287
 
280
288
  ## Provisioning and audit
281
289
 
@@ -283,23 +291,33 @@ Repository creation is opt-in twice: the provider must set `allow_creation: true
283
291
 
284
292
  Creation occurs during receive-pack discovery. Consequently, `git push --dry-run`, abandoned pushes and later branch-policy rejections may leave empty repositories. Clone/fetch never creates repositories. Direct receive-pack POSTs cannot create missing repositories. Created repositories are private and uninitialized; the gateway never changes an existing repository's visibility, deletes it, or silently seeds `main`.
285
293
 
286
- The local database records creation reservations, daily quotas, outcomes and stable repository IDs. Same-repository creation requests are serialized. Conflicts and ambiguous create responses are reconciled by lookup; unresolved attempts remain recorded for the next authorized discovery or operator investigation. Existing enrolled repository names cannot silently bind to a different provider ID after deletion/recreation. New JWT bindings can also explicitly pin an ID.
294
+ Concurrent creation is coordinated by the forge's repository-name uniqueness. Different gateway instances may both attempt creation; after a conflict or ambiguous response, gitvend looks up the name and checks that the resulting repository is private and matches the requested owner/name. It never changes an existing repository's visibility. If the outcome cannot be confirmed, the request fails with 503; a later normal push discovery performs a fresh lookup. There are no background reconciliation jobs, creation quotas or stored attempts.
295
+
296
+ Permissions follow repository names. Deleting and recreating a repository under the same name leaves a matching JWT grant applicable to that name. Gitvend does not enroll repositories or pin their IDs.
297
+
298
+ ### Audit logs
287
299
 
288
- Use a Git response's `X-Request-ID` to locate its recorded request in the audit journal. Audit intent is durable before a push or create is forwarded. Push audit distinguishes success, rejection, partial success and unknown outcomes; HTTP 200 alone is not success. An interrupted push is never automatically retried. A crash can leave an `admitted` record, which must be treated as an unknown outcome.
300
+ `gitvend serve` emits newline-delimited JSON audit records on **stdout** and operational logs on stderr. Collect stdout with your process supervisor or logging service. Gitvend keeps no audit database or outbox and has no `audit` command.
289
301
 
290
- To inspect the audit journal, stop the server and run the following. Output is one JSON event per line, including identity, repository, operation and outcome when available:
302
+ Each JSON record has an `audit` object containing request ID, issuer/subject, token ID, repository, operation and outcome when available. A request can emit an `admitted` event before contacting the upstream and a final outcome with the same `audit.id`. Creation events have a separate ID and an `audit.parent_id` linking them to the Git request's `X-Request-ID` response header. JWT strings, upstream tokens and pack contents are excluded.
303
+
304
+ For example, inspect an existing log export with `jq`:
291
305
 
292
306
  ```sh
293
- gitvend audit -state var/gitvend.db
307
+ jq -c 'select(.audit.operation == "git-receive-pack") | .audit' audit.jsonl
294
308
  ```
295
309
 
296
- The first release uses bbolt with an exclusive process lock: **run one gateway instance per state file**. It handles concurrent agents inside that instance. Active-active replicas need a shared transactional state implementation; separate local state files would not share quotas, identity enrollment or provisioning coordination. Preserve and back up the state file. Audit retention/compaction and a metrics export endpoint are not implemented yet.
310
+ Push audit distinguishes `accepted`, `rejected`, `partial` and `unknown`; HTTP 200 alone is not success. Creation outcomes are `created`, `reconciled`, `rejected` or `unknown` and do not claim that a subsequent push succeeded. A crash can leave an admission with no final record. Log delivery is best-effort, with retention and durability handled by your logging infrastructure. Inspect the remote refs before retrying an interrupted push; gitvend never retries a push automatically.
311
+
312
+ ### Multiple instances
313
+
314
+ Run any number of instances behind the same HTTPS endpoint with matching configuration, trusted keys and credentials. No shared disk, database, distributed lock or sticky session is required. Configuration/key rotations must reach each instance; a reload on one instance does not update others. `max_concurrent` and request limits apply per instance. If you need fleet-wide rate limits or creation budgets, enforce them outside gitvend. A metrics export endpoint is not implemented.
297
315
 
298
316
  ## Configuration and limits
299
317
 
300
- Run the service under a supervisor with a consistent working directory and durable state volume. `SIGTERM` or Ctrl-C starts graceful shutdown with a 10-second drain period; transfers still running afterward are closed.
318
+ Run the service under a supervisor with a consistent working directory and readable configuration/key/credential files. The running gateway can use a read-only filesystem; operator commands such as `keygen` and `sign -output` still write their explicitly requested files. `SIGTERM` or Ctrl-C starts graceful shutdown with a 10-second drain period; transfers still running afterward are closed.
301
319
 
302
- Send `SIGHUP` to atomically reload public keys, issuer ceilings, upstream token files, deny rules and limits. A failed reload preserves the last valid configuration. Changes to the listener, TLS paths or state-file path require restart. Replacing an environment variable requires restarting the process. Config reload does not rewrite credentials already in use by an admitted operation.
320
+ Send `SIGHUP` to atomically reload public keys, issuer ceilings, upstream token files, deny rules and limits. A failed reload preserves the last valid configuration. Changes to the listener or TLS paths require restart. Replacing an environment variable requires restarting the process. Config reload does not rewrite credentials already in use by an admitted operation.
303
321
 
304
322
  For signing-key rotation, add the new public key under a new `id`, reload, and issue tokens with that `-kid`. Keep the old key configured until its tokens have expired, including clock tolerance, if they should remain valid; then remove it and reload. To rotate an upstream file token, replace that file and reload; the gateway reads upstream credentials at startup/reload, unlike the agent helper, which reads its JWT for each credential request.
305
323
 
@@ -314,18 +332,16 @@ Important defaults:
314
332
  | `max_pack_bytes` | 1 GiB incoming encoded and decoded request body limits |
315
333
  | `max_concurrent` | 64 active Git requests |
316
334
  | `request_timeout_seconds` | 1,800 |
317
- | `creates_per_subject_per_day` | 100, grouped by issuer and subject |
318
- | `creates_per_owner_per_day` | 1,000 |
319
335
 
320
336
  The HTTP server accepts up to 64 KiB of headers. Basic authentication expands a JWT by roughly one-third, so ingress limits must accommodate the configured token budget too. Use `sign -max-token-bytes` with the same budget as the gateway. Oversized grants are rejected, never truncated or broadened.
321
337
 
322
- Creation quotas reserve capacity durably before calling the forge. Uncertain/failed creation attempts conservatively retain the reservation for that day; retrying the same recorded attempt does not reserve again. Existing repositories do not consume creation quota. Incoming packs are streamed after their command prefix passes authorization. Pack contents are not inspected for file/path rules or commit ancestry.
338
+ Incoming packs are streamed after their command prefix passes authorization. Pack contents are not inspected for file/path rules or commit ancestry.
323
339
 
324
- Authorized Git requests look up repository metadata through the forge API to verify repository identity. These calls share the upstream credential's API rate limit; this release does not cache metadata or coordinate rate limits across deployments.
340
+ Ordinary clone/fetch/push requests need only the upstream Git endpoint. REST lookups are used during push discovery when both the configuration and JWT permit automatic creation. Those API calls share the upstream credential's rate limit; gitvend does not coordinate rate limits across instances.
325
341
 
326
342
  ## Troubleshooting
327
343
 
328
- Start with `gitvend version`, `/healthz`, and `git ls-remote --heads` as shown above. Use the HTTP `X-Request-ID` for authenticated Git requests to correlate a failure with the gateway audit journal. Avoid sharing raw credentials or full authorization headers when collecting diagnostics.
344
+ Start with `gitvend version`, `/healthz`, and `git ls-remote --heads` as shown above. Use the HTTP `X-Request-ID` for authenticated Git requests to correlate a failure with the collected audit logs. Avoid sharing raw credentials or full authorization headers when collecting diagnostics.
329
345
 
330
346
  | Symptom | What to check |
331
347
  | --- | --- |
@@ -336,16 +352,16 @@ Start with `gitvend version`, `/healthz`, and `git ls-remote --heads` as shown a
336
352
  | HTTP 403 / `push denied for ...` | Inspect the exact destination ref, matching `r` plus `w`/`d` grants, issuer/owner limits and gateway denies. The sample denies writes to main/master. |
337
353
  | No refs shown; clone cannot find main or remote HEAD | The requested branch may be absent or hidden. Use an explicitly permitted branch with `--branch`; new scratch repos may have only `agents/47/start`. |
338
354
  | HTTP 404 / unsupported route or missing repository | Use `/alias/owner/repo.git`, check allowed owners, and verify the upstream identity can see the repository. Only authorized push discovery can create a missing repo. |
339
- | Missing repo is not created | Check `allow_creation`, `c`, overlapping branch `rw`, owner limits, quotas and upstream organization permissions. Check the audit journal for an unresolved attempt. |
355
+ | Missing repo is not created | Check `allow_creation`, `c`, overlapping branch `rw`, owner limits and upstream organization permissions. Check the audit logs for an unconfirmed create attempt. |
340
356
  | HTTP 429 / gateway busy | The concurrent-request limit is reached. Wait for active requests to finish and retry. |
341
- | HTTP 409 / creation quota exhausted or repository identity changed | Check creation quotas, prior enrollment and whether the upstream repository was deleted/recreated. Existing repositories do not consume creation quota. |
342
- | HTTP 502 / upstream request refused or verification failed | Check the upstream token, Git/API base URLs, forge availability/rate limits and repository identity. A gateway JWT cannot overcome upstream restrictions. |
343
- | HTTP 503 / audit or provisioning state unavailable | Check the state directory, disk space, permissions and database lock. Stop the server before using `audit`. |
357
+ | HTTP 409 / private profile violation | A creation attempt resolved to a non-private repository. Investigate the upstream name/visibility; gitvend will not alter it automatically. |
358
+ | HTTP 502 / upstream request refused or lookup failed | Check the upstream token, Git/API base URLs and forge availability/rate limits. A gateway JWT cannot overcome upstream restrictions. |
359
+ | HTTP 503 / repository creation unconfirmed | The create attempt and follow-up lookup could not confirm success. Check upstream access/availability, then retry normal push discovery. |
344
360
  | HTTP 431 or large token rejected at ingress | Align ingress header limits, server `max_token_bytes` and signing `-max-token-bytes`; Basic authentication expands the token. |
345
361
  | Push disconnects or audit outcome is `unknown` | Fetch or inspect the permitted remote ref before deciding whether to retry; some or all updates may already have reached GitHub. |
346
362
  | npm-installed command reports a missing binary | Check whether install scripts were disabled and whether release downloads were reachable; after fixing that, run `npm rebuild @colony2/gitvend`. |
347
363
 
348
- Deleting and recreating a GitHub repository under the same name changes its ID. Gitvend intentionally refuses to silently replace an enrolled ID. There is no enrollment-reset CLI in this release; investigate with the operator instead of deleting the entire state database, which also holds quotas and audit history.
364
+ When upgrading an earlier checkout, remove `state_file`, `creates_per_subject_per_day` and `creates_per_owner_per_day` from configuration. Remove `bindings` from grant files and reissue any JWTs containing that field. Unknown fields are rejected. Old database files are no longer read by the gateway.
349
365
 
350
366
  ## CLI reference
351
367
 
@@ -355,17 +371,16 @@ For commands that take flags, use `-h` to list them, for example `gitvend sign -
355
371
  | --- | --- |
356
372
  | `gitvend version` | Show the release version or development build identity. |
357
373
  | `gitvend serve -config gitvend.json` | Start the gateway. |
358
- | `gitvend keygen -private signing.key -public signing.pub` | Generate a new issuer key pair without overwriting existing files. |
374
+ | `gitvend keygen -private signing.key -public signing.pub` | Generate a new issuer key pair without overwriting existing files. Add `-if-missing` to validate/reuse persistent keys or recover a missing public key. Parent directories must exist. |
359
375
  | `gitvend sign` | Issue a JWT; requires `-key`, `-kid`, `-issuer`, `-subject` and a grant. Use repeated `-permission` **or** `-grant`, not both; `-output` writes an atomic token file. |
360
376
  | `gitvend credential` | Git helper; specify `-host` and exactly one of `-token-file` or `-token-env`, followed by `get`, `store` or `erase`. |
361
377
  | `gitvend explain` | Explain explicit `-permission` rules for a `-repo`, optional `-ref`, and `-action`. Ref values are fully qualified, such as `refs/heads/main`. |
362
- | `gitvend audit -state var/gitvend.db` | Print the stored audit journal while the gateway is stopped. |
363
378
 
364
379
  `explain` actions are `repo.read`, `repo.create`, `ref.discover`, `ref.create`, `ref.update` and `ref.delete`. The default is `ref.update`.
365
380
 
366
381
  ## Compatibility and verification scope
367
382
 
368
- The automated tests cover real Git v2 discovery/clone/fetch, shallow and partial clones, known-hash reads, standard pushes, ref filtering, mixed denied pushes, tags, deletion, force updates, atomic and partial push outcomes, creation races/recovery, JWT tampering/expiry/size, secret/key reloads, identity substitution and malformed/gzip requests. Parser fuzz targets and race checks are included.
383
+ The automated tests cover real Git v2 discovery/clone/fetch, shallow and partial clones, known-hash reads, standard pushes, ref filtering, mixed denied pushes, tags, deletion, force updates, atomic and partial push outcomes, creation races/recovery, JWT tampering/expiry/size, secret/key reloads, independent replicas, name-based permissions and malformed/gzip requests. Parser fuzz targets and race checks are included.
369
384
 
370
385
  Supported object format is SHA-1. SSH, Git LFS, Git protocol v0/v1 fetch, signed push certificates, push options, named shallow exclusions, ref-in-want, offloaded pack/bundle URLs and provider-managed ref namespaces are rejected or not advertised. Branch writes can include workflow files; CI execution and secrets remain governed by the forge's configuration. GitHub branch rules continue to apply to the shared upstream identity.
371
386
 
@@ -375,12 +390,12 @@ The original requirements and implementation tradeoffs are in [the design](docs/
375
390
 
376
391
  ## Release automation
377
392
 
378
- The [release workflow](.github/workflows/release.yaml) follows [c2j's release pattern](https://github.com/colony-2/c2j/blob/main/.github/workflows/release.yaml). Successful pushes to `main` in the `colony-2` organization run the test suite, automatically bump and push a version tag (patch by default), build four binaries, and publish GitHub release archives and checksums. It then publishes `@colony2/gitvend` to npm after installing and smoke-testing the packed wrapper against the published assets. There is no container-image build or publication.
393
+ The [release workflow](.github/workflows/release.yaml) follows [c2j's release pattern](https://github.com/colony-2/c2j/blob/main/.github/workflows/release.yaml). Successful pushes to `main` in the `colony-2` organization run the test suite, automatically bump and push a version tag (patch by default), build four binaries, and publish GitHub release archives and checksums. It then publishes `@colony2/gitvend` to npm after installing and smoke-testing the packed wrapper against the published assets. It also publishes Linux amd64/arm64 container images to `ghcr.io/colony-2/gitvend` with the release tag and `latest`, using `GITHUB_TOKEN` with package-write permission.
379
394
 
380
395
  GitHub release/tag publication uses the workflow's `GITHUB_TOKEN`. Configure npm trusted publishing for `colony-2/gitvend`, workflow `release.yaml`, or provide an `NPM_TOKEN` with publishing access to `@colony2/gitvend`. The npm package must exist and its trusted publisher must be configured before token-free publication can work; bootstrap it with a token if needed.
381
396
 
382
397
  macOS signing/notarization is optional. Set all five secrets to enable it: `MACOS_SIGN_P12`, `MACOS_SIGN_PASSWORD`, `APPLE_API_ISSUER`, `APPLE_API_KEY_ID`, and `APPLE_API_KEY`. No signing secrets skips this step; a partial configuration fails the build. Signing requires the matching Apple certificate and notarization credentials.
383
398
 
384
- The reusable [test workflow](.github/workflows/test.yml) runs Go race tests, vet, parser fuzzing and npm installer tests. Run the installer tests locally with `npm test --prefix npm/gitvend`. Release archive smoke tests also check all checksums and run the native binary's `version` command before upload.
399
+ The reusable [test workflow](.github/workflows/test.yml) runs Go race tests, vet, parser fuzzing, npm installer tests, and the Docker Compose smoke test. Run the installer tests locally with `npm test --prefix npm/gitvend` and the container checks with `make test-compose`. Release archive smoke tests also check all checksums and run the native binary's `version` command before upload.
385
400
 
386
401
  Copied release tooling retains its upstream Apache-2.0 license and attribution in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). This repository does not yet declare a project-wide license; npm metadata uses `UNLICENSED`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colony2/gitvend",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Git HTTPS gateway with signed repository and branch permissions",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/colony-2/gitvend",