From ca6ce6ea95a84fc5fc9f411e878ecbe2078fcac9 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 17 Mar 2026 17:57:18 +0100 Subject: [PATCH 1/2] fix: document global flags in RPC API reference (#1084) - endpoints.go: add GlobalOptions() to extract root command flags, filtering out CLI-only ones (--repo-dir, --config, --debug, etc.) - formatter.go: add GenerateGlobalOptionsBlock to Formatter interface, call it in GenerateDocs before per-endpoint sections - markdown.go: render "Global options" section with query param flags (--offline, --cid-base, --encoding, --timeout, etc.) and a separate "Authentication" subsection for --api-auth (HTTP header) - endpoints_test.go: add TestGlobalOptions covering inclusion, exclusion, and api-auth separation --- tools/http-api-docs/endpoints.go | 55 +++++++++++++++++++++++++++ tools/http-api-docs/endpoints_test.go | 47 ++++++++++++++++++++++- tools/http-api-docs/formatter.go | 15 +++++++- tools/http-api-docs/markdown.go | 39 +++++++++++++++++++ 4 files changed, 154 insertions(+), 2 deletions(-) diff --git a/tools/http-api-docs/endpoints.go b/tools/http-api-docs/endpoints.go index 8b4087a76..3729fffb3 100644 --- a/tools/http-api-docs/endpoints.go +++ b/tools/http-api-docs/endpoints.go @@ -92,6 +92,61 @@ func IPFSVersion() string { return config.CurrentVersionNumber } +// cliOnlyOptions lists root-level flags that only apply to the CLI client +// and are not meaningful when passed as query parameters to the HTTP RPC API. +// For example, --repo-dir and --config-file refer to local filesystem paths, +// --api tells the CLI which daemon to connect to, and --help/--debug control +// local CLI behavior. +var cliOnlyOptions = map[string]struct{}{ + corecmds.RepoDirOption: {}, + corecmds.ConfigFileOption: {}, + corecmds.ConfigOption: {}, + corecmds.DebugOption: {}, + corecmds.LocalOption: {}, // deprecated alias for --offline + corecmds.ApiOption: {}, + "help": {}, // cmds.OptLongHelp + "h": {}, // cmds.OptShortHelp +} + +// GlobalOptions extracts the options defined on corecmds.Root that are +// relevant to the HTTP RPC API. The Root command itself has Run == nil so +// Endpoints() skips it, but its Options slice contains global flags +// (--offline, --timeout, --encoding, etc.) that can be passed as query +// parameters to any endpoint. CLI-only flags are filtered out. +// +// The --api-auth flag is a special case: it controls RPC authentication +// but is sent as an HTTP Authorization header, not a query parameter. +// It is returned separately so the formatter can document it differently. +func GlobalOptions() (queryOpts []*Argument, authOpt *Argument) { + for _, opt := range corecmds.Root.Options { + name := opt.Names()[0] + if _, skip := cliOnlyOptions[name]; skip { + continue + } + + def := fmt.Sprint(opt.Default()) + if def == "" { + def = "" + } + + arg := &Argument{ + Name: name, + Type: opt.Type().String(), + Description: opt.Description(), + Default: def, + } + + // api-auth is sent as an HTTP header, not a query parameter + if name == corecmds.ApiAuthOption { + authOpt = arg + continue + } + + queryOpts = append(queryOpts, arg) + } + return queryOpts, authOpt +} + // Endpoints receives a name and a go-ipfs command and returns the endpoints it // defines] (sorted). It does this by recursively gathering endpoints defined by // subcommands. Thus, calling it with the core command Root generates all diff --git a/tools/http-api-docs/endpoints_test.go b/tools/http-api-docs/endpoints_test.go index 51bec3d24..a43d42c37 100644 --- a/tools/http-api-docs/endpoints_test.go +++ b/tools/http-api-docs/endpoints_test.go @@ -1,7 +1,52 @@ package docs -import "testing" +import ( + "slices" + "testing" +) func TestEndpoints(t *testing.T) { AllEndpoints() } + +func TestGlobalOptions(t *testing.T) { + queryOpts, authOpt := GlobalOptions() + + // Verify we got some query parameter options + if len(queryOpts) == 0 { + t.Fatal("expected at least one global query parameter option") + } + + // Collect names for easier assertions + names := make([]string, len(queryOpts)) + for i, opt := range queryOpts { + names[i] = opt.Name + } + + // Key RPC-relevant flags should be present + for _, expected := range []string{"offline", "cid-base", "encoding", "timeout"} { + if !slices.Contains(names, expected) { + t.Errorf("expected global option %q not found in %v", expected, names) + } + } + + // CLI-only flags should be filtered out + for _, excluded := range []string{"repo-dir", "config-file", "config", "debug", "local", "api", "help", "h"} { + if slices.Contains(names, excluded) { + t.Errorf("CLI-only option %q should not appear in global RPC options", excluded) + } + } + + // api-auth should be returned as the separate auth option + if authOpt == nil { + t.Fatal("expected api-auth to be returned as authOpt") + } + if authOpt.Name != "api-auth" { + t.Errorf("expected authOpt.Name == \"api-auth\", got %q", authOpt.Name) + } + + // api-auth should not appear in the query parameter list + if slices.Contains(names, "api-auth") { + t.Error("api-auth should not appear in query parameter options") + } +} diff --git a/tools/http-api-docs/formatter.go b/tools/http-api-docs/formatter.go index d6c7b2540..09b3451e9 100644 --- a/tools/http-api-docs/formatter.go +++ b/tools/http-api-docs/formatter.go @@ -9,6 +9,10 @@ import ( // Formatter allows to implement generation of docs in different formats. type Formatter interface { GenerateIntro() string + // GenerateGlobalOptionsBlock documents global options that apply to all + // endpoints. queryOpts are passed as URL query parameters; authOpt (if + // non-nil) is the authentication option sent via HTTP header instead. + GenerateGlobalOptionsBlock(queryOpts []*Argument, authOpt *Argument) string GenerateStatusIntro(status cmds.Status) string GenerateIndex(endp []*Endpoint) string GenerateEndpointBlock(endp *Endpoint) string @@ -18,11 +22,20 @@ type Formatter interface { GenerateExampleBlock(endp *Endpoint) string } -// GenerateDocs uses a formatter to generate documentation for every endpoint +// GenerateDocs uses a formatter to generate documentation for every endpoint. +// It first emits the intro and global options sections, then iterates through +// endpoints grouped by status (active, experimental, deprecated, removed). func GenerateDocs(api []*Endpoint, formatter Formatter) string { buf := new(bytes.Buffer) buf.WriteString(formatter.GenerateIntro()) + // Document global options from the root command before per-endpoint docs. + // These are flags like --offline, --timeout, --encoding that apply to + // every RPC endpoint but were previously missing from the reference. + // See https://github.com/ipfs/ipfs-docs/issues/1084 + queryOpts, authOpt := GlobalOptions() + buf.WriteString(formatter.GenerateGlobalOptionsBlock(queryOpts, authOpt)) + for _, status := range []cmds.Status{cmds.Active, cmds.Experimental, cmds.Deprecated, cmds.Removed} { endpoints := InStatus(api, status) if len(endpoints) == 0 { diff --git a/tools/http-api-docs/markdown.go b/tools/http-api-docs/markdown.go index db8295119..8b253cf91 100644 --- a/tools/http-api-docs/markdown.go +++ b/tools/http-api-docs/markdown.go @@ -189,6 +189,45 @@ The API will return HTTP Error 403 when Origin is missing, does not match the AP return buf.String() } +// GenerateGlobalOptionsBlock produces a markdown section documenting global +// options that apply to every RPC endpoint. These are defined on Kubo's root +// command but were previously omitted because the root command has no handler +// of its own (Run == nil). Query parameter options (--offline, --timeout, etc.) +// are listed as a standard arguments block. The --api-auth option is documented +// separately since it is sent as an HTTP Authorization header, not a query param. +func (md *MarkdownFormatter) GenerateGlobalOptionsBlock(queryOpts []*Argument, authOpt *Argument) string { + buf := new(bytes.Buffer) + + fmt.Fprint(buf, `## Global options + +The following options apply to all endpoints and can be passed as query parameters on every request. +These correspond to global flags on the `+"`ipfs`"+` CLI (e.g. `+"`--offline`"+`, `+"`--encoding`"+`, `+"`--timeout`"+`). + +`) + + for _, opt := range queryOpts { + fmt.Fprint(buf, genArgument(opt, false)) + } + + // Document the api-auth option separately: it is relevant to the RPC API + // but travels as an HTTP header rather than a URL query parameter. + if authOpt != nil { + fmt.Fprintf(buf, ` +### Authentication + +The `+"`--%s`"+` option is used for [RPC API authorization](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations). +Unlike the query parameter options above, it must be sent as an HTTP header: + +`+"```"+` +Authorization: Bearer +`+"```"+` + +`, authOpt.Name) + } + + return buf.String() +} + func (md *MarkdownFormatter) GenerateStatusIntro(status cmds.Status) string { return fmt.Sprintf(` ## %s RPC commands From b2431ba6721e1a9bd21b192ddec30d98ac82383e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 17 Mar 2026 19:42:31 +0100 Subject: [PATCH 2/2] fix: hide deprecated --upgrade-cidv0-in-output from RPC reference - endpoints.go: add to ignoredGlobalOptions (now redundant with --cid-base) - endpoints_test.go: update assertions - markdown.go: use --cid-base in example text - rpc.md: regenerated, updated --cid-base description to note CIDv0 auto-upgrade behavior --- docs/reference/kubo/rpc.md | 20 +++++++++++++++++++- tools/http-api-docs/endpoints.go | 27 ++++++++++++++++++--------- tools/http-api-docs/endpoints_test.go | 13 +++++++++---- tools/http-api-docs/markdown.go | 2 +- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/docs/reference/kubo/rpc.md b/docs/reference/kubo/rpc.md index 0ab0a2733..4ca8cd3a0 100644 --- a/docs/reference/kubo/rpc.md +++ b/docs/reference/kubo/rpc.md @@ -47,7 +47,7 @@ I AM SERIOUS, DO NOT EDIT ANYTHING BELOW ;-D --> -::: tip Generated on 2026-03-10, from kubo v0.40.1 +::: tip Generated on 2026-03-17, from kubo v0.40.1 This document was autogenerated from [v0.40.1](https://github.com/ipfs/kubo/releases/tag/v0.40.1). For issues and support, check out the [http-api-docs](https://github.com/ipfs/ipfs-docs/tree/main/tools/http-api-docs) generator on GitHub. ::: @@ -157,6 +157,24 @@ A `405` error may mean that you are using the wrong HTTP method (i.e. GET instea When a request is sent from a browser, HTTP RPC API follows the [Origin-based security model](https://en.wikipedia.org/wiki/Same-origin_policy), and expects the `Origin` HTTP header to be present. The API will return HTTP Error 403 when Origin is missing, does not match the API port, or is not safelisted via `API.HTTPHeaders.Access-Control-Allow-Origin` in the config. +## Global options + +The following options apply to all endpoints and can be passed as query parameters on every request. +These correspond to global flags on the `ipfs` CLI (e.g. `--offline`, `--cid-base`, `--timeout`). + +- `offline` [bool]: Run the command offline. Required: no. +- `cid-base` [string]: Multibase encoding for CIDs in output. CIDv0 is automatically converted to CIDv1 when a base other than base58btc is specified. Required: no. +- `timeout` [string]: Set a global timeout on the command. Required: no. + +### Authentication + +The `--api-auth` option is used for [RPC API authorization](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations). +Unlike the query parameter options above, it must be sent as an HTTP header: + +``` +Authorization: Bearer +``` + ## RPC commands diff --git a/tools/http-api-docs/endpoints.go b/tools/http-api-docs/endpoints.go index 3729fffb3..754792b06 100644 --- a/tools/http-api-docs/endpoints.go +++ b/tools/http-api-docs/endpoints.go @@ -92,12 +92,17 @@ func IPFSVersion() string { return config.CurrentVersionNumber } -// cliOnlyOptions lists root-level flags that only apply to the CLI client -// and are not meaningful when passed as query parameters to the HTTP RPC API. -// For example, --repo-dir and --config-file refer to local filesystem paths, -// --api tells the CLI which daemon to connect to, and --help/--debug control -// local CLI behavior. -var cliOnlyOptions = map[string]struct{}{ +// ignoredGlobalOptions lists root-level flags that should not appear in the +// HTTP RPC API reference. This includes: +// - CLI-only flags (--repo-dir, --config-file, --api, --debug, --help) +// that refer to local paths or control CLI behavior +// - --encoding: the HTTP handler defaults to JSON and this is already +// documented in the "Flags" intro section of the reference +// - --stream-channels: parsed by the HTTP handler but has no effect; +// HTTP response streaming is determined by the response type, not this flag +// - --upgrade-cidv0-in-output: deprecated, --cid-base with a non-base58btc +// value now automatically upgrades CIDv0 to CIDv1 +var ignoredGlobalOptions = map[string]struct{}{ corecmds.RepoDirOption: {}, corecmds.ConfigFileOption: {}, corecmds.ConfigOption: {}, @@ -106,13 +111,17 @@ var cliOnlyOptions = map[string]struct{}{ corecmds.ApiOption: {}, "help": {}, // cmds.OptLongHelp "h": {}, // cmds.OptShortHelp + cmds.EncLong: {}, // --encoding: HTTP defaults to JSON, documented in intro + cmds.ChanOpt: {}, // --stream-channels: no-op over HTTP + "upgrade-cidv0-in-output": {}, // deprecated: --cid-base auto-upgrades for non-base58btc } // GlobalOptions extracts the options defined on corecmds.Root that are // relevant to the HTTP RPC API. The Root command itself has Run == nil so // Endpoints() skips it, but its Options slice contains global flags -// (--offline, --timeout, --encoding, etc.) that can be passed as query -// parameters to any endpoint. CLI-only flags are filtered out. +// (--offline, --timeout, --cid-base, etc.) that can be passed as query +// parameters to any endpoint. Flags listed in ignoredGlobalOptions are +// filtered out. // // The --api-auth flag is a special case: it controls RPC authentication // but is sent as an HTTP Authorization header, not a query parameter. @@ -120,7 +129,7 @@ var cliOnlyOptions = map[string]struct{}{ func GlobalOptions() (queryOpts []*Argument, authOpt *Argument) { for _, opt := range corecmds.Root.Options { name := opt.Names()[0] - if _, skip := cliOnlyOptions[name]; skip { + if _, skip := ignoredGlobalOptions[name]; skip { continue } diff --git a/tools/http-api-docs/endpoints_test.go b/tools/http-api-docs/endpoints_test.go index a43d42c37..6afb8900b 100644 --- a/tools/http-api-docs/endpoints_test.go +++ b/tools/http-api-docs/endpoints_test.go @@ -24,16 +24,21 @@ func TestGlobalOptions(t *testing.T) { } // Key RPC-relevant flags should be present - for _, expected := range []string{"offline", "cid-base", "encoding", "timeout"} { + for _, expected := range []string{"offline", "cid-base", "timeout"} { if !slices.Contains(names, expected) { t.Errorf("expected global option %q not found in %v", expected, names) } } - // CLI-only flags should be filtered out - for _, excluded := range []string{"repo-dir", "config-file", "config", "debug", "local", "api", "help", "h"} { + // Ignored flags should be filtered out: CLI-only flags, plus encoding + // (HTTP defaults to JSON, documented in intro), stream-channels + // (no-op over HTTP), and upgrade-cidv0-in-output (deprecated). + for _, excluded := range []string{ + "repo-dir", "config-file", "config", "debug", "local", "api", "help", "h", + "encoding", "stream-channels", "upgrade-cidv0-in-output", + } { if slices.Contains(names, excluded) { - t.Errorf("CLI-only option %q should not appear in global RPC options", excluded) + t.Errorf("ignored option %q should not appear in global RPC options", excluded) } } diff --git a/tools/http-api-docs/markdown.go b/tools/http-api-docs/markdown.go index 8b253cf91..a76991d50 100644 --- a/tools/http-api-docs/markdown.go +++ b/tools/http-api-docs/markdown.go @@ -201,7 +201,7 @@ func (md *MarkdownFormatter) GenerateGlobalOptionsBlock(queryOpts []*Argument, a fmt.Fprint(buf, `## Global options The following options apply to all endpoints and can be passed as query parameters on every request. -These correspond to global flags on the `+"`ipfs`"+` CLI (e.g. `+"`--offline`"+`, `+"`--encoding`"+`, `+"`--timeout`"+`). +These correspond to global flags on the `+"`ipfs`"+` CLI (e.g. `+"`--offline`"+`, `+"`--cid-base`"+`, `+"`--timeout`"+`). `)