{"id":20595705,"url":"https://github.com/firetail-io/firetail-go-lib","last_synced_at":"2026-02-08T19:43:01.748Z","repository":{"id":64298166,"uuid":"545395694","full_name":"FireTail-io/firetail-go-lib","owner":"FireTail-io","description":"Go middlewares for the Firetail SaaS","archived":false,"fork":false,"pushed_at":"2025-01-13T15:12:56.000Z","size":465,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-14T23:43:55.029Z","etag":null,"topics":["chi","go","golang","observability","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FireTail-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-04T09:41:18.000Z","updated_at":"2025-01-13T15:12:21.000Z","dependencies_parsed_at":"2024-02-07T12:52:08.243Z","dependency_job_id":"ac45d783-e018-4ab9-b14a-5a8665e254d3","html_url":"https://github.com/FireTail-io/firetail-go-lib","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FireTail-io%2Ffiretail-go-lib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FireTail-io%2Ffiretail-go-lib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FireTail-io%2Ffiretail-go-lib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FireTail-io%2Ffiretail-go-lib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FireTail-io","download_url":"https://codeload.github.com/FireTail-io/firetail-go-lib/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248981259,"owners_count":21193143,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chi","go","golang","observability","rest-api"],"created_at":"2024-11-16T08:13:53.859Z","updated_at":"2026-02-08T19:43:01.717Z","avatar_url":"https://github.com/FireTail-io.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Firetail Go Library\n\n[![License](https://img.shields.io/pypi/l/firetail.svg)](https://github.com/FireTail-io/firetail-go-lib/blob/main/LICENSE.txt) [![Go Reference](https://pkg.go.dev/badge/github.com/FireTail-io/firetail-go-lib#section-readme.svg)](https://pkg.go.dev/github.com/FireTail-io/firetail-go-lib#section-readme) [![codecov](https://codecov.io/gh/FireTail-io/firetail-go-lib/branch/main/graph/badge.svg?token=QZX8OSE964)](https://codecov.io/gh/FireTail-io/firetail-go-lib)\n\nMiddlewares providing request and response validation against an OpenAPI spec at runtime, optionally integrating with the Firetail SaaS. Packages containing middleware for various different frameworks can be found in the [middlewares](./middlewares) directory, and examples of their use in [examples](./examples).\n\n\n\n## Getting Started\n\n### Middleware for `net/http`\n\nGet the middleware:\n\n```bash\ngo get github.com/FireTail-io/firetail-go-lib/middlewares/http\n```\n\nImport it:\n\n```go\nimport firetail \"github.com/FireTail-io/firetail-go-lib/middlewares/http\"\n```\n\nCreate a middleware using `GetMiddleware`; see the `Options` struct for all the available configurations:\n\n```go\nfiretailMiddleware, err := firetail.GetMiddleware(\n\t\u0026firetail.Options{\n\t\tOpenapiSpecPath: path,\n\t\tLogApiKey:       apiToken,\n\t},\n)\nif err != nil {\n\t// Handle the err...\n}\n```\n\nYou will then have a `func(next http.Handler) http.Handler`, `firetailMiddleware`, which you can use to wrap a `http.Handler` just the same as with the middleware from [`net/http/middleware`](https://pkg.go.dev/go.ntrrg.dev/ntgo/net/http/middleware). This should also be suitable for [Chi](https://go-chi.io/#/pages/middleware).\n\nSee the [Go reference for the Options struct](https://pkg.go.dev/github.com/FireTail-io/firetail-go-lib@v0.0.0/middlewares/http#Options) for documentation regarding the available options. For example, if you are using `us.firetail.app` you will need to set the `LogsApiUrl` to `https://api.logging.us-east-2.prod.firetail.app/logs/bulk`.\n\n\n\n## Tests\n\nAutomated testing is setup with the `testing` package, using [github.com/stretchr/testify](https://pkg.go.dev/github.com/stretchr/testify) for shorthand assertions. You can run them with `go test`.\n\n\n\n## XML Support\n\nThe Firetail Go library does not come with XML request \u0026 response body decoding support out of the box. You will need to implement your own decoder as an [openapi3filter.BodyDecoder](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter#BodyDecoder) and pass it to Firetail as part of the `CustomBodyDecoders` field of the `firetail.Options` struct. See the following example for a minimal XML decoder setup using [sbabiv/xml2map](https://github.com/sbabiv/xml2map):\n\n```go\nmiddleware, err := firetail.GetMiddleware(\u0026firetail.Options{\n\tOpenapiSpecPath: \"./app-spec.yaml\",\n\tCustomBodyDecoders: map[string]openapi3filter.BodyDecoder{\n\t\t\"application/xml\": func(r io.Reader, h http.Header, sr *openapi3.SchemaRef, ef openapi3filter.EncodingFn) (interface{}, error) {\n\t\t\treturn xml2map.NewDecoder(r).Decode()\n\t\t},\n\t},\n})\n```\n\n\n\n## Authentication\n\nIf you use `securitySchemes` in your OpenAPI specification, you will need to populate the `firetail.Options` struct's `AuthCallbacks` field with a callback for each security scheme implementing your authentication logic.\n\nFor example, for the following `securitySchemes`:\n\n```yaml\ncomponents:\n  securitySchemes:\n    MyBasicAuth:\n      type: http\n      scheme: basic\n```\n\nYour `AuthCallback` could look like this:\n\n```go\nAuthCallbacks: map[string]func(context.Context, *openapi3filter.AuthenticationInput){\n\t\"MyBasicAuth\": func(ctx context.Context, ai *openapi3filter.AuthenticationInput) error {\n\t\ttoken := ai.RequestValidationInput.Request.Header.Get(\"Authorization\")\n\t\treturn validateBasicAuthToken(token)\n\t},\n},\n```\n\n\n\n### Custom Auth Error Responses\n\nIn order to customise the errors returned by your application when a request fails to authenticate, you can pick up the errors returned by your `AuthCallbacks` in a custom `ErrHandler`. This allows you to, for example, add the `WWW-Authenticate` header on responses to requests that fail to validate against a basic auth security requirement:\n\n```go\n// We'll use this err when the basic auth fails to validate.\nvar BasicAuthErr = errors.New(\"invalid authorization token\")\n\nfiretailMiddleware, err := firetail.GetMiddleware(\u0026firetail.Options{\n\tOpenapiSpecPath: \"app-spec.yaml\",\n\n\t// First, let's write our auth callback which, if the name of the security scheme it's being asked to check\n\t// is 'MyBasicAuth', will check that the Authorization header contains the b64 encoding of 'admin:password'.\n\tAuthCallbacks: map[string]func(context.Context, *openapi3filter.AuthenticationInput){\n\t\t\"MyBasicAuth\": func(ctx context.Context, ai *openapi3filter.AuthenticationInput) error {\n\t\t\ttoken := ai.RequestValidationInput.Request.Header.Get(\"Authorization\")\n\t\t\tif token != \"Basic YWRtaW46cGFzc3dvcmQ=\" {\n\t\t\t\treturn BasicAuthErr\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t},\n\n\t// Then, in our ErrCallback, we can check if the error it's received is a security error. If it is, and \n\t// one of its suberrs is a BasicAuthErr, then we can add the WWW-Authenticate header to the response.\n\tErrCallback: func(err firetail.ErrorAtRequest, w http.ResponseWriter, r *http.Request) {\n\t\tif securityErr, isSecurityErr := err.(firetail.ErrorAuthNoMatchingSchema); isSecurityErr {\n\t\t\tfor _, subErr := range securityErr.Err.Errors {\n\t\t\t\tif subErr == BasicAuthErr {\n\t\t\t\t\tw.Header().Add(\"WWW-Authenticate\", \"Basic\")\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tw.Header().Add(\"Content-Type\", \"text/plain\")\n\t\tw.WriteHeader(err.StatusCode())\n\t\tw.Write([]byte(err.Error()))\n\t},\t\n})\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiretail-io%2Ffiretail-go-lib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffiretail-io%2Ffiretail-go-lib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffiretail-io%2Ffiretail-go-lib/lists"}