> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scanoss.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Usage

> Using the SCANOSS Go SDK: the decoration pipeline, per-service progress reporting, logging, and scanning from Go code.

## The Decoration Pipeline

Beyond scanning, `pkg/scanoss` is the SDK for the SCANOSS decoration services, the same ones behind [SCANOSS-CLI's decoration commands](/en/latest/cli/scanoss-cli/decoration-commands). The **pipeline** runs a configurable set of them over the same PURLs (Package URLs, the `pkg:type/namespace/name@version` scheme identifying each component) in parallel, reports per-service progress, and returns one object keyed by service. Chunking and the worker pool are handled internally.

```go theme={null}
import "github.com/scanoss/scanoss.go/pkg/scanoss"

client, err := scanoss.New(scanoss.Config{
	APIKey:    os.Getenv("SCANOSS_API_KEY"),
	ChunkSize: 20, // PURLs per request
	Workers:   10, // max concurrent requests
})
if err != nil {
	return err
}

comps := scanoss.Components("pkg:github/scanoss/engine")

pipe := client.DecorationPipeline(
	scanoss.ServiceVulnerabilities,
	scanoss.ServiceLicenses,
)
pipe.Add(scanoss.ServiceCryptographyAlgorithms, scanoss.ServiceGeoprovenanceOrigin)

res, err := pipe.Run(context.Background(), comps)
if err != nil {
	log.Fatal(err) // only if every service failed
}
fmt.Println(res.String())

for svc, e := range res.Errors { // per-service failures are recorded, not fatal
	log.Printf("%s failed: %v", svc, e)
}
```

`pipe.Run` only returns an error when every service in the pipeline failed. A partial failure, one service down, the rest fine, comes back as a successful result with the failing service's error recorded in `res.Errors`, so one bad service doesn't discard the results you did get.

## Component Version Requirements

`scanoss.Components(...)`, used above, is a shorthand that builds a component list from bare PURLs, no version attached. That's fine for lookups where the latest or any version will do. When a version matters, build the components directly instead:

```go theme={null}
comps := []scanoss.Component{
	{Purl: "pkg:github/scanoss/engine", Requirement: "4.17.21"},
	{Purl: "pkg:github/scanoss/engine", Requirement: "5.4.7"},
}
```

Either form of `comps` works anywhere a decoration call takes one, the pipeline, a single service, or `WithDecorationReporter`, shown next.

## Per-Service Progress

Implement `DecorationReporter` and hand it to the call. Every update carries the service that produced it, so one receiver renders them all, and since services run concurrently, it must be safe for concurrent use:

```go theme={null}
type bars struct{ mu sync.Mutex }

func (b *bars) Decorating(service string, done, total int) {
	b.mu.Lock()
	defer b.mu.Unlock()
	fmt.Printf("%-26s %d/%d purls\n", service, done, total)
}

res, err := pipe.Run(ctx, comps, scanoss.WithDecorationReporter(&bars{}))
```

The per-service methods take it too:

```go theme={null}
res, err := client.Vulnerabilities.Components(ctx, comps, scanoss.WithDecorationReporter(&bars{}))
```

## A Single Service, Without the Pipeline

Each decoration service is a grouped handle on the client, useful when you only need one and the pipeline's parallel fan-out would be overkill:

```go theme={null}
res, err := client.Vulnerabilities.Components(ctx, comps) // *scanossapi.VulnerabilitiesResponse
// also: client.Licenses.Attribution, client.Cryptography.Algorithms,
//       client.Geoprovenance.Origin, client.Copyright.Evidence, ...
```

## Scanning from the SDK

```go theme={null}
client, err := scanoss.New(scanoss.Config{APIKey: os.Getenv("SCANOSS_API_KEY")})
result, err := client.Scan.Folder(ctx, "./my-project")
// resume by id: client.Scan.Wait(ctx, scanID)
```

## Logging

The SDK writes nothing until you ask it to, it won't put lines in your program's output uninvited. One call covers every package it's built from, not just the client:

```go theme={null}
scanoss.SetLogger(slog.Default())      // fold into your own stream
scanoss.SetLogger(slog.New(myHandler)) // keep them apart, or drop them
scanoss.SetLogger(nil)                 // back to silence
```

At `Debug` this also explains file selection, which rules a collection applied, and which rule excluded each file:

```
level=DEBUG msg="filters applied"  builtinFolderRules=true gitignore=true matchers=51
level=DEBUG msg="file excluded"    path=CHANGELOG.md rule=ext:.md
level=DEBUG msg="directory pruned" path=node_modules rule=dir:node_modules
```

<Note>
  Call `SetLogger` during initialisation: it's process-wide, so changing it
  while calls are in flight can split a run's output across two
  destinations.
</Note>

## Request Tuning

Beyond `APIKey`, `APIURL`, and the proxy/TLS fields covered in [Authentication](authentication), `Config` exposes the request behaviour itself:

| Field                | Default | Behaviour                                                                                                                                                    |
| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ChunkSize`          | `10`    | PURLs per decoration request, shared by every decoration service.                                                                                            |
| `Workers`            | `5`     | Max concurrent requests. Never larger than the number of chunks.                                                                                             |
| `Timeout`            | `120s`  | Bounds one request attempt, body transfer included. A negative value disables it. Retry-After waits happen between attempts, so this doesn't cut them short. |
| `MaxRetries`         | `5`     | Caps retries for a transient failure, a network error, a truncated response, or a `429`/`5xx` status. A negative value disables retries entirely.            |
| `RetryBackoffBase`   | `250ms` | The first wait the SDK computes itself, doubled per attempt. A `Retry-After` the server sent takes precedence over it.                                       |
| `MaxServerRetryWait` | `5m`    | Caps a single `Retry-After` wait, bounding a pathological server value. Doesn't bound `RetryBackoffBase`'s own waits.                                        |

All are zero-value-safe: an unset field falls back to its default, so you only need to set what you're overriding.
