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 8b4087a76..754792b06 100644 --- a/tools/http-api-docs/endpoints.go +++ b/tools/http-api-docs/endpoints.go @@ -92,6 +92,70 @@ func IPFSVersion() string { return config.CurrentVersionNumber } +// 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: {}, + corecmds.DebugOption: {}, + corecmds.LocalOption: {}, // deprecated alias for --offline + 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, --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. +// 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 := ignoredGlobalOptions[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..6afb8900b 100644 --- a/tools/http-api-docs/endpoints_test.go +++ b/tools/http-api-docs/endpoints_test.go @@ -1,7 +1,57 @@ 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", "timeout"} { + if !slices.Contains(names, expected) { + t.Errorf("expected global option %q not found in %v", expected, names) + } + } + + // 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("ignored 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..a76991d50 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`"+`, `+"`--cid-base`"+`, `+"`--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