Skip to content

Commit da1aa39

Browse files
committed
feat: release public Hensu machine-local config
Ship v0.2.0-ready tree: peek by default, no bulk read, exec, topic guides, repo scaffolding, and origin story (EN/ES).
0 parents  commit da1aa39

89 files changed

Lines changed: 7183 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/CODEOWNERS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @MY-RV

‎.github/ISSUE_TEMPLATE/bug.yml‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Bug report
2+
description: Something broken or unexpected
3+
labels: ["bug"]
4+
body:
5+
- type: textarea
6+
id: what
7+
attributes:
8+
label: What happened?
9+
description: Expected vs actual
10+
validations:
11+
required: true
12+
- type: textarea
13+
id: repro
14+
attributes:
15+
label: Reproduction
16+
description: Minimal steps, a config file snippet with the values replaced, OS/arch
17+
validations:
18+
required: true
19+
- type: input
20+
id: version
21+
attributes:
22+
label: hensu --version
23+
validations:
24+
required: true
25+
- type: markdown
26+
attributes:
27+
value: |
28+
**Never paste real values.** Replace them, or report with `hensu get` output,
29+
which is already masked. If the bug *is* that a value leaked, say so and
30+
report it privately instead: see [SECURITY.md](../blob/main/SECURITY.md).

‎.github/ISSUE_TEMPLATE/feature.yml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Feature request
2+
description: Something hensu should do and does not
3+
labels: ["enhancement"]
4+
body:
5+
- type: textarea
6+
id: problem
7+
attributes:
8+
label: What are you trying to do?
9+
description: The task, not the feature you imagined for it
10+
validations:
11+
required: true
12+
- type: textarea
13+
id: today
14+
attributes:
15+
label: How do you do it today?
16+
description: The workaround, even if it is ugly. This is the strongest argument a request can carry.
17+
validations:
18+
required: true
19+
- type: textarea
20+
id: idea
21+
attributes:
22+
label: What would it look like?
23+
description: Optional. A command line you wish worked.

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## Summary
2+
3+
<!-- What does this PR change, and why? -->
4+
5+
## Checklist
6+
7+
- [ ] Behavior change? Update [docs/contract.md](docs/contract.md) in the same PR
8+
- [ ] Tests cover the change (happy path + edge)
9+
- [ ] `godo ci` passes locally
10+
- [ ] No value ever reaches stdout that the reveal mode should have hidden
11+
- [ ] No drive-by refactors

‎.github/workflows/ci.yml‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-go@v5
13+
with:
14+
go-version-file: go.mod
15+
- name: Bootstrap godo
16+
run: go install github.com/my-rv/godo/cmd/godo@v0.2.0
17+
- name: CI gate (godo.yaml)
18+
run: $(go env GOPATH)/bin/godo ci
19+
20+
# Catches a broken .goreleaser.yaml on every push, instead of at tag time
21+
# when a bad config means a failed release.
22+
release-config:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- uses: actions/checkout@v4
26+
- uses: actions/setup-go@v5
27+
with:
28+
go-version-file: go.mod
29+
- uses: goreleaser/goreleaser-action@v6
30+
with:
31+
version: "~> v2"
32+
args: check

‎.github/workflows/release.yml‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
# Signed build provenance for every published artifact.
11+
id-token: write
12+
attestations: write
13+
14+
jobs:
15+
release:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
- uses: actions/setup-go@v5
22+
with:
23+
go-version-file: go.mod
24+
- name: Bootstrap godo
25+
run: go install github.com/my-rv/godo/cmd/godo@v0.2.0
26+
- name: Verify tag tests
27+
run: $(go env GOPATH)/bin/godo ci
28+
- uses: goreleaser/goreleaser-action@v6
29+
with:
30+
version: "~> v2"
31+
args: release --clean
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
# Optional: absent secret → tap/bucket push is skipped, not failed.
35+
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
36+
- name: Attest build provenance
37+
uses: actions/attest-build-provenance@v2
38+
with:
39+
subject-path: |
40+
dist/hensu_*
41+
dist/checksums.txt

‎.gitignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Local build artifacts. Anchored to the root on purpose: a bare "hensu" also
2+
# matches the cmd/hensu package directory, which is the shipped CLI.
3+
/hensu
4+
/dist/
5+
*.exe
6+
*.test
7+
*.out
8+

‎.goreleaser.yaml‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
2+
version: 2
3+
4+
project_name: hensu
5+
6+
before:
7+
hooks:
8+
- go mod tidy
9+
10+
builds:
11+
- id: hensu
12+
main: ./cmd/hensu
13+
binary: hensu
14+
env:
15+
- CGO_ENABLED=0
16+
goos:
17+
- linux
18+
- darwin
19+
- windows
20+
goarch:
21+
- amd64
22+
- arm64
23+
ignore:
24+
- goos: windows
25+
goarch: arm64
26+
ldflags:
27+
- -s -w -X github.com/my-rv/hensu.Version={{.Version}}
28+
29+
archives:
30+
- id: archive
31+
formats: [tar.gz]
32+
format_overrides:
33+
- goos: windows
34+
formats: [zip]
35+
name_template: >-
36+
{{ .ProjectName }}_
37+
{{- .Version }}_
38+
{{- .Os }}_
39+
{{- .Arch }}
40+
- id: binary
41+
formats: [binary]
42+
name_template: >-
43+
{{ .ProjectName }}_
44+
{{- .Version }}_
45+
{{- .Os }}_
46+
{{- .Arch }}
47+
48+
checksum:
49+
name_template: checksums.txt
50+
51+
# Packaging homes. Both pushes need a PAT with write access to those repos,
52+
# exposed to the release workflow as TAP_GITHUB_TOKEN; without it the release
53+
# still publishes, it just does not update the tap or the bucket.
54+
# Homebrew ships pre-built binaries as casks now; `brews` is deprecated and
55+
# makes `goreleaser check` fail. Casks are macOS-only — Linux installs go
56+
# through `go install` or the release binary (docs/install.md).
57+
homebrew_casks:
58+
- name: hensu
59+
ids: [archive]
60+
repository:
61+
owner: MY-RV
62+
name: homebrew-tap
63+
branch: main
64+
token: "{{ .Env.TAP_GITHUB_TOKEN }}"
65+
directory: Casks
66+
homepage: "https://github.com/MY-RV/hensu"
67+
description: "Machine-local config get/set/dump that agents can read without seeing values"
68+
skip_upload: '{{ if .Env.TAP_GITHUB_TOKEN }}false{{ else }}true{{ end }}'
69+
hooks:
70+
post:
71+
install: |
72+
if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0
73+
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/hensu"]
74+
end
75+
76+
scoops:
77+
- name: hensu
78+
ids: [archive]
79+
repository:
80+
owner: MY-RV
81+
name: scoop-bucket
82+
branch: main
83+
token: "{{ .Env.TAP_GITHUB_TOKEN }}"
84+
homepage: "https://github.com/MY-RV/hensu"
85+
description: "Machine-local config get/set/dump that agents can read without seeing values"
86+
license: "Apache-2.0"
87+
skip_upload: '{{ if .Env.TAP_GITHUB_TOKEN }}false{{ else }}true{{ end }}'
88+
89+
changelog:
90+
sort: asc
91+
filters:
92+
exclude:
93+
- "^docs:"
94+
- "^test:"
95+
96+
release:
97+
draft: false
98+
prerelease: auto

‎CHANGELOG.md‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Changelog
2+
3+
## [0.2.0] — unreleased
4+
5+
### Security
6+
- **`--update` verifies SHA-256 against the release `checksums.txt` before
7+
replacing the running binary.** It previously installed whatever bytes the
8+
download returned. A release without checksums, an asset missing from the
9+
file, or a digest mismatch now aborts without touching the executable.
10+
11+
### Changed
12+
- **No bulk read.** `hensu` with no command prints usage and exits 2 instead of dumping the
13+
config; to see a value you name its key.
14+
- **JSON is the only output encoding.** The `json` and `yaml` verbs and the
15+
`export KEY=value` dump are gone, along with `Store.DumpExport` / `DumpJSON` / `DumpYAML`.
16+
YAML and JSON remain first-class as *file* formats, which is where they matter.
17+
- `update.Client.Latest` returns a `Candidate` (asset + checksums).
18+
- Public API is a thin facade (`pkg.go`); domain lives in `internal/store` (godo-style layout).
19+
- Canonical Go module layout (`cmd/hensu`, `internal/`, import `github.com/my-rv/hensu`).
20+
- `Store` and `FileSystem` methods take `context.Context` (cancelable lock acquire / IO checks).
21+
- CLI exit codes: `0` ok, `2` invalid usage, `1` runtime error.
22+
- File locking always uses a stable sidecar `<path>.hensu-lock` (correct with atomic rename).
23+
- GoReleaser + tag-triggered GitHub release workflow.
24+
25+
### Added
26+
- **`peek` is the default reveal mode**, and the zero value of `Reveal`. A caller that
27+
configures nothing still cannot print a secret whole: long values come back as
28+
`abcd***wxyz`, short ones as `***`. Seeing a value in full is a grant — `-r trust` — and
29+
grants are asked for, by a human or a model alike.
30+
- `-r` / `--reveal` with its own vocabulary (`peek` / `mask` / `trust`) replacing the old
31+
`get` / `def` / `spy` verbs. One axis, one spelling: the strictness lattice the three verbs
32+
needed is gone with them.
33+
- `hensu exec [--] CMD [ARGS...]` — runs a command with the config in its environment
34+
(config wins over inherited values), forwards the child exit code, and skips keys that are
35+
not valid environment variable names. The reveal mode is not propagated to children.
36+
- `read KEY` requires `-r trust`.
37+
- `HENSU_REVEAL` sets the mode for a session; an unparseable value is an error, never a
38+
silent fallback to raw output.
39+
- Cross-process `Set` locking, durable atomic writes (`Sync` + dir sync).
40+
- Typed errors, godoc examples, fuzz tests, benchmarks, GitHub Actions CI (incl. staticcheck).
41+
- Apache-2.0 LICENSE, docs (contract, security, versioning, architecture, install, release).
42+
- `scripts/release-local.sh` for multi-arch artifacts without upload.
43+
- CONTRIBUTING.md.
44+
- CLI self-update: `--update` / `--update-check` (`internal/update`, `HENSU_RELEASES_API`).
45+
- `godo.yaml` dogfood scripts (`test`/`vet`/`build`/`check`/`ci`/`release-local`).
46+
- GoReleaser publishes bare binaries (for `--update`) in addition to archives.
47+
- Homebrew cask and Scoop manifest published by GoReleaser (`MY-RV/homebrew-tap`
48+
under `Casks/`, `MY-RV/scoop-bucket`); skipped rather than failed when the PAT
49+
is absent. Casks are macOS-only — Linux installs via `go install` or the
50+
release binary.
51+
- Signed build provenance attestations for every released artifact.
52+
- `goreleaser check` in CI, so a broken release config fails on push, not at tag.
53+
- `SECURITY.md` (private vulnerability reporting) and `docs/agents.md`.
54+
55+
### Notes
56+
- Pre-1.0: APIs may still change. Treat `v0.x` as evolving.

‎CONTRIBUTING.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Contributing
2+
3+
## Before a change
4+
5+
1. Read [docs/contract.md](./docs/contract.md) — behavior changes need a contract update in the same PR.
6+
2. Keep the cut: thin facade + `internal/store`; CLI talks to the facade only.
7+
8+
## Loop
9+
10+
```bash
11+
godo ci
12+
# or
13+
go test -race ./...
14+
go build -o hensu ./cmd/hensu
15+
./hensu --version
16+
```
17+
18+
## PR shape
19+
20+
- One concern per PR.
21+
- Tests for the behavior you touch (happy path **and** the edge that failed or could fail).
22+
- No drive-by refactors.
23+
24+
## Code
25+
26+
See [docs/standards.md](./docs/dev/standards.md).

0 commit comments

Comments
 (0)