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

# Quickstart

> Build a minimal connector end to end: module, manifest, adapter, wiring, and image

This walkthrough adds a connector called `example` in the `vulnerabilities` category. Replace `vulnerabilities` and `example` with the category and slug you want.

## Prerequisites

* Go 1.26+
* [Task](https://taskfile.dev) 3.x
* Docker 20.10+ (to build and run the image)

## 1. Create the module

The directory is `<category>/<slug>`.

```bash theme={null}
mkdir -p vulnerabilities/example
cd vulnerabilities/example
```

Create `go.mod` requiring the SDK, then point it at the local copy with a `replace`:

```go theme={null}
module github.com/oasm-platform/oasm-connectors/vulnerabilities/example

go 1.26

require github.com/oasm-platform/oasm-connectors/sdk v0.0.0

replace github.com/oasm-platform/oasm-connectors/sdk => ../../sdk
```

```bash theme={null}
GOWORK=off go mod tidy
```

## 2. Write `manifest.yaml`

```yaml theme={null}
name: Example
slug: example
version: 1.0.0
image: ghcr.io/oasm-platform/connector-example:1.0.0
author: 'you'
pricingTier: [free]
shortDescription: 'Example vulnerability scanner'
description: 'Scans a target for example findings.'
capabilities: [vulnerabilities]
inputsSchema:
  type: object
  required: [target]
  properties:
    target:
      type: string
      format: uri
      title: Scan target
      description: 'Target URL or hostname to scan.'
  additionalProperties: false
configSchema:
  type: object
  additionalProperties: false
  properties:
    severity:
      type: string
      enum: [info, low, medium, high, critical]
      title: Minimum severity
      default: high
```

See the [manifest reference](/connectors/manifest) for every field.

## 3. Write the adapter

`adapter.go` — the only tool-specific code.

```go theme={null}
package main

import (
    "context"
    "fmt"
    "os"
    "strings"

    "github.com/oasm-platform/oasm-connectors/sdk/connector"
)

type ExampleAdapter struct{}

// Validate rejects inputs the tool cannot run.
func (a *ExampleAdapter) Validate(_ context.Context, inputs map[string]any) error {
    target, _ := inputs["target"].(string)
    if strings.TrimSpace(target) == "" {
        return fmt.Errorf("fatal: target is required")
    }
    return nil
}

// Execute runs the tool and streams findings to out.
func (a *ExampleAdapter) Execute(ctx context.Context, inputs map[string]any, out chan<- connector.Finding) error {
    target, _ := inputs["target"].(string)
    severity := os.Getenv("OASM_CONFIG") // parse in real connectors

    _ = severity
    for _, raw := range runTool(ctx, target) {
        f := connector.Finding{
            Name:     raw.Name,
            Severity: normalizeSeverity(raw.Severity),
            Host:     target,
        }
        select {
        case out <- f:
        case <-ctx.Done():
            return ctx.Err()
        }
    }
    return nil
}

func normalizeSeverity(s string) string {
    switch strings.ToLower(strings.TrimSpace(s)) {
    case "info", "low", "medium", "high", "critical":
        return strings.ToLower(strings.TrimSpace(s))
    default:
        return "info"
    }
}
```

See the [Adapter reference](/connectors/adapter) and [Finding reference](/connectors/finding).

## 4. Wire `main.go`

`main.go` only constructs the adapter and starts the runtime. Treat a cancelled context as a clean exit.

```go theme={null}
package main

import (
    "context"
    "log"

    sdkconn "github.com/oasm-platform/oasm-connectors/sdk/connector"
    "github.com/oasm-platform/oasm-connectors/sdk/runtime"
)

func main() {
    conn := sdkconn.New(&ExampleAdapter{})
    rt := runtime.New(conn)

    log.Println("example connector starting...")
    if err := rt.Run(context.Background()); err != nil && err != context.Canceled {
        log.Fatalf("connector failed: %v", err)
    }
    log.Println("example connector stopped")
}
```

<Info>
  Everything between `runtime.New` and `Run` — dialing, registration, the stream — is the SDK's job. You do not configure it.
</Info>

## 5. Write the `Dockerfile`

The build context is the **repo root**, so copy `sdk/` and the connector directory explicitly. Drop both `go.mod`/`go.sum` first so dependency layers cache.

```dockerfile theme={null}
FROM golang:1.26-alpine AS builder
WORKDIR /src
COPY sdk/go.mod sdk/go.sum sdk/
COPY vulnerabilities/example/go.mod vulnerabilities/example/go.sum vulnerabilities/example/
RUN cd vulnerabilities/example && GOWORK=off go mod download
COPY sdk/ sdk/
COPY vulnerabilities/example/ vulnerabilities/example/
RUN cd vulnerabilities/example && CGO_ENABLED=0 GOWORK=off go build -trimpath -o /out/connector .

FROM alpine:3.20
COPY --from=builder /out/connector /usr/local/bin/connector
RUN adduser -D -u 10001 connector
USER 10001
ENTRYPOINT ["/usr/local/bin/connector"]
```

The image must not expose ports or require a shell.

## 6. Regenerate and test

```bash theme={null}
task manifest   # regenerate manifest.json
task test       # per-module tests
task vet        # per-module go vet
```

## Next steps

<CardGroup cols={2}>
  <Card title="Build & test" icon="package" href="/connectors/build-and-test">
    Task commands, testing rules, and publishing.
  </Card>

  <Card title="Inputs & config" icon="sliders" href="/connectors/inputs-and-config">
    Read the config profile the right way.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.