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
72 changes: 59 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,60 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed
## [0.9.1] - 2026-10-09

### Security

- **v0.9.0 binaries were built with Go 1.27.1, which has 9 published
vulnerabilities in code c1i calls. Upgrade.** This release builds with Go
1.27.2 and `golang.org/x/net` 0.60.0, fixing 13 vulnerabilities in Go
1.27.1's standard library and `x/net` 0.59.0's `http2`, 9 of them in code
c1i calls: GO-2026-6617, -6613, -6612, -6611, -6610, -6609, -6608, -6607,
-6605, -6604, -6603, -6600 and -6599. `google.golang.org/grpc` moves from
1.83.2 to 1.84.0, and `grpc-gateway` and `timestamp-authority` to their
latest releases.

### Upgrading from 0.9.0

No flags change. The OpenAPI spec cache moves to
`~/.c1i/cache/api-openapi.yaml`, so the first offline `docs openapi`,
`docs endpoints` or `docs endpoint` after upgrading fails until one fetch
succeeds. A spec refresh deletes any other file in `~/.c1i/cache/`, and a
write to the file token cache deletes the expired and unusable token files in
its tokens dir. The `docs endpoints --filter` no-match message no longer
names `c1i mcp` or `access-reviews`, since the spec now covers both.

### Changed

- **API discovery uses the current public API contract**, including MCP server,
tool, and toolset operations. `docs openapi`, `docs endpoints`, and
`docs endpoint` fetch `https://www.c1.ai/api/openapi.yaml` and use a
source-specific cache at `~/.c1i/cache/api-openapi.yaml`.

### Fixed

- **`docs endpoint` output changed from run to run.** A `$ref` shared by
several fields was expanded under whichever one a random map order reached
first, and left bare under the rest. The order is now fixed, so the same
endpoint prints the same schema every time.

- **A non-spec response could poison the OpenAPI cache for 24 hours.** A 200
whose body isn't an OpenAPI document, such as a captive portal's sign-in
page, was cached and served as the spec. Now only a body that parses as one
is cached; otherwise the existing cache is used, or the command fails. A
download that fails partway falls back the same way, and the cache file is
replaced atomically. Any fallback to an expired cache now prints a one-line
warning on stderr with the cache's age and the failure.

- **With no home dir, the `docs` OpenAPI commands cached the spec in the
working directory**, under `./.c1i/cache/`. They now fetch the spec and use
it without a cache.

- **Old cache files stayed on disk forever.** A cache file renamed or dropped
by a release, such as the `openapi.yaml` the entry above replaces, was never
removed. Refreshing the OpenAPI spec now deletes any file in `~/.c1i/cache/`
that the running version doesn't use. It leaves subdirectories alone, and
skips the sweep when the cache dir is a symlink or, with no home dir, a path
relative to the working directory.
by a release, such as the `openapi.yaml` that `api-openapi.yaml` replaces,
was never removed. Refreshing the OpenAPI spec now deletes any file in
`~/.c1i/cache/` that the running version doesn't use. It leaves
subdirectories alone, and skips the sweep when the cache dir is a symlink.

- **Stale access-token files stayed on disk forever.** Without a usable OS
keyring, c1i caches one token file per set of credentials under
Expand All @@ -33,12 +74,16 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
it takes a protobuf duration in seconds, such as `86400s`, matching
`tasks update-grant-duration` and `entitlements create --duration-grant`.

- **Builds now use Go 1.27.2 and `golang.org/x/net` 0.60.0**, fixing 13
published vulnerabilities in Go 1.27.1's standard library and `x/net`
0.59.0's `http2`, 9 of them in code c1i calls: GO-2026-6617, -6613, -6612,
-6611, -6610, -6609, -6608, -6607, -6605, -6604, -6603, -6600 and -6599.
`google.golang.org/grpc` moves from 1.83.2 to 1.84.0, and `grpc-gateway`
and `timestamp-authority` to their latest releases.
- **The docs didn't say service principal delete and credential revoke are
soft.** `service-principals get` on a deleted principal and
`service-principals credentials get` on a revoked credential still return
the record with exit 0 and no field marking it. Their help and the README
now say so: the lists omit them, and `users get` shows a deleted principal
as `DELETED`.

- The README's bash completion example wrote to `/etc/bash_completion.d`,
which needs root; it now installs per user. `docs endpoints --filter` help
now says it also matches operation descriptions.

## [0.9.0] - 2026-10-06

Expand Down Expand Up @@ -1880,7 +1925,8 @@ First changelog entry; releases through v0.1.5 predate this file (see the

- CI enforces `gofmt` via golangci-lint; module-wide formatting normalized.

[Unreleased]: https://github.com/ConductorOne/c1i/compare/v0.9.0...HEAD
[Unreleased]: https://github.com/ConductorOne/c1i/compare/v0.9.1...HEAD
[0.9.1]: https://github.com/ConductorOne/c1i/releases/tag/v0.9.1
[0.9.0]: https://github.com/ConductorOne/c1i/releases/tag/v0.9.0
[0.8.0]: https://github.com/ConductorOne/c1i/releases/tag/v0.8.0
[0.7.0]: https://github.com/ConductorOne/c1i/releases/tag/v0.7.0
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,11 @@ c1i service-principals credentials update <credential-id> --service-principal-id
c1i service-principals credentials revoke <credential-id> --service-principal-id <sp-id>
```

Delete and revoke are soft: `get` on a deleted principal and `credentials get`
on a revoked credential still return the record, exit 0, with no field marking
it (the principal's embedded `user.status` stays `ENABLED`). Only the lists
omit them; `users get <sp-id>` shows `"status": "DELETED"` and `deletedAt`.

`--expires` takes a positive Go duration (e.g. `720h`). The server accepts the
range `(0s, 4320h]` — up to 180 days — rejecting more with `value must be inside
range (0s, 4320h0m0s]`. Fractional seconds are preserved.
Expand Down Expand Up @@ -869,7 +874,7 @@ c1i docs guide
c1i docs guide register-mcp-server
```

`docs openapi`, `docs endpoints`, and `docs endpoint` use the current public API contract at `https://www.c1.ai/api/openapi.yaml`, including MCP server, tool, and toolset operations. The spec is cached for 24 hours at `~/.c1i/cache/api-openapi.yaml`; fetch failures fall back to that cache.
`docs openapi`, `docs endpoints`, and `docs endpoint` use the current public API contract at `https://www.c1.ai/api/openapi.yaml`, including MCP server, tool, and toolset operations. The spec is cached for 24 hours at `~/.c1i/cache/api-openapi.yaml`; fetch failures fall back to that cache, however old, with a one-line warning on stderr naming its age.

`docs search` is a semantic search with no relevance threshold: every query returns up to 10 nearest matches, so even a nonsense query comes back with plausible-looking hits. A returned hit is not proof a concept exists, and an unexpected hit is not proof the thing you searched for is absent — read the snippet, or fetch the page with `docs page`, to judge. To check whether an API endpoint exists, use `docs endpoints --filter`, which has a real no-match.

Expand Down Expand Up @@ -1324,7 +1329,8 @@ invocation.

```sh
# bash
c1i completion bash > /etc/bash_completion.d/c1i
mkdir -p ~/.local/share/bash-completion/completions
c1i completion bash > ~/.local/share/bash-completion/completions/c1i

# zsh
c1i completion zsh > "${fpath[1]}/_c1i"
Expand Down
17 changes: 12 additions & 5 deletions cmd/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,13 @@ Nor do those five call the same place, which matters for egress rules and for
why one can fail while another works: `docs openapi`, `docs endpoints` and
`docs endpoint` fetch `https://www.c1.ai/api/openapi.yaml` (cached 24h at
`~/.c1i/cache/api-openapi.yaml`, so a run can return rows without sending a
request), while `docs search` and `docs page` call a third party — `api.mintlify.com` —
with a public client-side key. `docs search` is semantic with no relevance
threshold, so it always returns up to 10 plausible hits even for a nonsense
query: never read a result as proof a concept exists, or an unexpected result
as proof it is absent. `docs endpoints --filter` DOES have a true no-match.
request), while `docs search` and `docs page` call a third party —
`api.mintlify.com` — with a public client-side key. `docs search` is semantic
with no relevance threshold, so it always returns up to 10 plausible hits even
for a nonsense query: never read a result as proof a concept exists, or an
unexpected result as proof it is absent. `docs endpoints --filter` DOES have a
true no-match. If a fetch fails, an expired cache is served with a one-line
`Warning:` on stderr naming its age.

Don't store files in `~/.c1i/cache/`: a spec refresh deletes any file there
that the running version doesn't use.
Expand Down Expand Up @@ -452,6 +454,11 @@ resource with `--resource-id` likewise means you drop
exit `0` with `deletedAt` set. So a `deleted_at` in an `access-profiles list`
row is null in practice — don't read the null as "not deleted", check with a
get.
- `service-principals delete` and `service-principals credentials revoke` are
soft: `get` and `credentials get` still return the record at exit `0` with no
field marking it (the principal's `user.status` stays `ENABLED`). Only the
lists omit them; `users get <sp-id>` shows `"status": "DELETED"` and
`deletedAt`.

## Upgrading c1i

Expand Down
4 changes: 2 additions & 2 deletions cmd/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ var cacheFiles = map[string]bool{cacheFileName: true}

// pruneCacheDir is best-effort: cleanup must never fail the command. It skips
// subdirectories, a symlinked dir (Lstat sees a link, not a dir) whose target
// may hold files c1i doesn't own, and a relative dir, which is what an unset
// HOME yields and would sweep the working directory instead.
// may hold files c1i doesn't own, and a relative dir, which would sweep the
// working directory instead.
func pruneCacheDir(dir string) {
if !filepath.IsAbs(dir) {
return
Expand Down
Loading
Loading