steam

package module
v0.0.0-...-b621bad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 21 Imported by: 0

README

go-steam

A Go library for downloading Steam app content and Workshop items without the Steam client.

Features

  • Anonymous downloads for publicly accessible apps (no account required)
  • Authenticated downloads for paid games and Workshop items
  • OS filtering auto-detects the current platform by default, so a single call never mixes Windows/Linux/macOS depots in the same target dir
  • Restores the executable bit on the platform's launch script/binary via PICS launch metadata when the depot manifest doesn't set it
  • Incremental updates — only downloads chunks that differ from what is already on disk
  • Concurrent chunk downloads with configurable parallelism
  • Supports VZip-ZSTD, VZip-LZMA, ZIP, and ZLib chunk formats
  • Steam Guard support: TOTP authenticator (automatic) and email (via callback)
  • Local cache for sessions, depot keys, and CDN tokens across runs

Requirements

Go 1.26+

Usage

Library
import steam "github.com/BirknerAlex/go-steam"

ctx := context.Background()

client, err := steam.New(ctx, steam.Config{
    // Omit Username/Password for anonymous access (free/public apps only).
    Username: "myaccount",
    Password: "mypassword",
    // SteamGuardCallback is called when Steam Guard is required.
    // Use steam.InteractiveSteamGuard to prompt stdin, or provide your own.
    SteamGuardCallback: steam.InteractiveSteamGuard,
    // CachePath defaults to ~/.cache/go-steam
})
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// Download a Steam app.
ch, err := client.DownloadApp(ctx, steam.AppDownloadRequest{
    AppID:     1623730,   // Palworld
    OS:        "linux",
    TargetDir: "/opt/palworld",
})
if err != nil {
    log.Fatal(err)
}

for p := range ch {
    if p.Err != nil {
        log.Fatal(p.Err)
    }
    fmt.Printf("[%s] %d/%d chunks\n", p.Phase, p.DoneChunks, p.TotalChunks)
}
// Download a Steam Workshop item (requires authenticated session).
ch, err := client.DownloadWorkshopItem(ctx, steam.WorkshopDownloadRequest{
    ItemID:    3625223587,
    TargetDir: "/tmp/workshop-item",
})
CLI — app download
go run ./cmd/app-download \
  -app 1623730 \
  -os linux \
  -username myaccount \
  -password mypassword \
  -output /opt/palworld \
  -v

Flags:

Flag Default Description
-app Steam App ID
-depots all Comma-separated depot IDs to restrict download
-branch public Branch name
-branch-password Password for protected beta branches
-os empty (auto-detect) OS filter: linux, windows, or macos; empty auto-detects the current platform. Downloads fail up front if this doesn't resolve to one of the three
-lang all Language filter (e.g. english)
-username Steam account username (omit for anonymous)
-password Steam account password
-totp-secret Base64 TOTP shared secret for automatic Steam Guard
-output ./output Destination directory
-validate false Verify on-disk files without re-downloading
-cache ~/.cache/go-steam Cache directory
-v false Verbose logging
CLI — workshop download
go run ./cmd/workshop-download \
  -item 3625223587 \
  -username myaccount \
  -password mypassword \
  -output /tmp/workshop \
  -v

Flags:

Flag Default Description
-item Workshop Published File ID
-username Steam account username (required)
-password Steam account password
-totp-secret Base64 TOTP shared secret for Steam Guard
-output ./output Destination directory
-cache ~/.cache/go-steam Cache directory
-v false Verbose logging
CLI — Steam Guard code
go run ./cmd/guard -secret <base64-totp-secret>

How it works

  1. Connects to a Steam CM (Connection Manager) server over WebSocket (TLS)
  2. Performs an AES encryption handshake using Steam's RSA public key
  3. Authenticates anonymously or via the Steam Authentication v2 flow (RSA-encrypted password → optional Steam Guard → access token)
  4. Caches the access token to disk so subsequent runs skip re-authentication
  5. Fetches app info via the PICS protocol to discover depots and manifest GIDs
  6. Obtains a manifest request code from the CM (required for modern CDN auth)
  7. Downloads and decrypts the depot manifest (AES-CBC, then LZMA decompression)
  8. Diffs the manifest against existing on-disk content using per-chunk SHA-1
  9. Downloads only the missing/changed chunks from Steam CDN servers concurrently
  10. For each chunk: decrypts (AES-256 ECB-IV+CBC), detects format (VZip-ZSTD / VZip-LZMA / ZIP / ZLib), decompresses, verifies SHA-1, and writes to disk

Limitations

  • Workshop downloads require an authenticated session with ownership of the parent app
  • Email-based Steam Guard works interactively via SteamGuardCallback; TOTP is fully automatic with -totp-secret
  • Anonymous sessions only work for apps that expose anonymous depot access
  • Branch password support is wired up but not widely tested

Acknowledgements

This project stands on a great deal of knowledge from the SteamKit project — the wire formats, the CM protocol, the manifest and depot-chunk layouts (VZip-LZMA, VZip-ZSTD, PKZip), and Steam's AES symmetric scheme were all learned from SteamKit's implementation and documentation. Several of our regression tests are direct ports of SteamKit's own test fixtures (real depot chunks, manifests, and PICS packets) so our decoders can be validated against the same ground truth. We are very grateful to the SteamKit maintainers and contributors for their excellent, long-running work.

License

This project's source code is licensed under the MIT License.

The binary test fixtures vendored under internal/*/testdata/ are copied from SteamKit's test suite and remain under the LGPL-2.1 license — they are used only by go test and are never compiled into the library. See THIRD_PARTY_NOTICES.md for full attribution.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotOwned is returned when the authenticated account does not own the
	// requested app or depot.
	ErrNotOwned = errors.New("steam: account does not own app/depot")

	// ErrSteamGuardRequired is returned when the account has Steam Guard enabled
	// and the login cannot proceed without a guard code.
	ErrSteamGuardRequired = errors.New("steam: steam guard code required")

	// ErrInvalidCredentials is returned when username/password are rejected.
	ErrInvalidCredentials = errors.New("steam: invalid credentials")

	// ErrRateLimited is returned when Steam throttles our requests.
	ErrRateLimited = errors.New("steam: rate limited by Steam")

	// ErrSessionNotReady is returned when the CM session has not reached the
	// Ready state before the context deadline expires.
	ErrSessionNotReady = errors.New("steam: CM session not ready")

	// ErrChunkCorrupt is returned when a downloaded chunk fails SHA1 verification.
	ErrChunkCorrupt = errors.New("steam: chunk SHA1 mismatch — data corrupt")

	// ErrManifestDecrypt is returned when manifest decryption or decompression fails.
	ErrManifestDecrypt = errors.New("steam: manifest decrypt/decompress failed")

	// ErrNoCMServers is returned when the CM server list cannot be fetched.
	ErrNoCMServers = errors.New("steam: no CM servers available")

	// ErrWorkshopItemNotFound is returned when the WebAPI cannot find the
	// requested workshop item.
	ErrWorkshopItemNotFound = errors.New("steam: workshop item not found")

	// ErrDepotKeyDenied is returned when Steam denies the depot decryption key
	// request (access denied / not owned).
	ErrDepotKeyDenied = errors.New("steam: depot key access denied")

	// ErrEncryptionHandshake is returned when the CM encryption handshake fails.
	ErrEncryptionHandshake = errors.New("steam: CM encryption handshake failed")
)

Sentinel errors returned by Client methods.

Functions

This section is empty.

Types

type AppDownloadRequest

type AppDownloadRequest struct {
	// AppID is the Steam application ID to download.
	AppID uint32

	// DepotIDs optionally restricts which depots to download. When nil, all
	// depots eligible for the current platform and language are downloaded.
	DepotIDs []uint32

	// Branch is the beta branch name. Leave empty or "public" for the default branch.
	Branch string

	// BranchPassword is required for password-protected beta branches.
	BranchPassword string

	// OS filters depots by target OS: "windows", "linux", or "macos". Leave
	// empty to auto-detect the OS this process is running on (via
	// runtime.GOOS). Depots without OS metadata (shared/common depots) are
	// always included regardless of this filter.
	//
	// DownloadApp rejects the request before downloading anything if OS is
	// set to a value other than the three above, or if it's empty and
	// runtime.GOOS isn't one Steam supports (e.g. freebsd) -- there is no
	// "download every platform" fallback.
	OS string

	// Language filters language depots. Leave empty for no language filtering.
	Language string

	// TargetDir is the directory where content will be written.
	// Existing files are diffed against the manifest; only changed chunks are downloaded.
	TargetDir string

	// ValidateOnly skips writing files — only verifies on-disk chunks against the manifest.
	ValidateOnly bool
}

AppDownloadRequest describes a Steam app content download.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the entry point for Steam content downloads. Create one with New(); use DownloadApp or DownloadWorkshopItem to fetch content.

func New

func New(ctx context.Context, cfg Config) (*Client, error)

New creates a Client and establishes CM connections. It blocks until both sessions are ready or ctx is cancelled.

func (*Client) Close

func (c *Client) Close()

Close shuts down all CM sessions gracefully.

func (*Client) DownloadApp

func (c *Client) DownloadApp(ctx context.Context, req AppDownloadRequest) (<-chan Progress, error)

DownloadApp downloads (or updates) the content of a Steam app. It returns a channel of Progress events; the channel is closed when the download completes or fails.

The caller must drain the channel. Cancelling ctx stops the download and closes the channel.

func (*Client) DownloadWorkshopItem

func (c *Client) DownloadWorkshopItem(ctx context.Context, req WorkshopDownloadRequest) (<-chan Progress, error)

DownloadWorkshopItem downloads a Steam Workshop item into req.TargetDir. An authenticated session is required; workshop items are never anonymous.

type Config

type Config struct {
	// Username and Password are required for authenticated downloads (apps that
	// require ownership, or workshop items).
	// Leave empty to use an anonymous session (public free-to-play games only).
	Username string
	Password string

	// CachePath is the directory used to persist sessions, depot keys, and CDN
	// tokens between runs.  Defaults to the OS temp directory if empty.
	CachePath string

	// CellID is the Steam CDN cell used for server selection.  0 selects automatically.
	CellID uint32

	// MaxParallelChunks is the maximum number of concurrent chunk downloads.
	// Defaults to 16 if zero.
	MaxParallelChunks int64

	// MaxParallelManifests is the maximum number of manifests downloaded concurrently.
	// Defaults to 4 if zero.
	MaxParallelManifests int64

	// SteamGuardCallback is called when Steam requires a Guard code during login.
	// Use InteractiveSteamGuard(), SteamGuardCodeGenerate(secret), or a custom
	// func — for example one that fetches and parses the guard email automatically.
	// Defaults to UnknownSteamGuard() which returns an error, so Steam Guard
	// failures are surfaced immediately rather than hanging.
	SteamGuardCallback SteamGuardCallback

	// CMServers overrides the CM server list (useful for testing).
	CMServers []string

	// Log is the logger used by the client.  Defaults to slog.Default().
	Log *slog.Logger
}

Config controls Client behaviour.

type Phase

type Phase int

Phase describes the current stage of a download operation.

const (
	PhaseResolving   Phase = iota // resolving app/workshop metadata
	PhaseManifest                 // fetching and parsing manifests
	PhaseDiffing                  // comparing on-disk state vs manifest
	PhaseDownloading              // downloading and writing chunks
	PhaseComplete                 // download finished successfully
)

func (Phase) String

func (p Phase) String() string

type Progress

type Progress struct {
	Phase Phase

	// TotalBytes is the total uncompressed bytes to download (0 until diffing is done).
	TotalBytes int64
	// DoneBytes is the number of uncompressed bytes written so far.
	DoneBytes int64

	// TotalChunks is the total number of chunks to fetch (0 until diffing is done).
	TotalChunks int
	// DoneChunks is the number of chunks successfully written.
	DoneChunks int

	// CurrentFile is the file path currently being written (relative to TargetDir).
	CurrentFile string

	// Err is non-nil when the download failed. The progress channel is closed
	// immediately after emitting a Progress with Err set.
	Err error
}

Progress is emitted on the channel returned by DownloadApp / DownloadWorkshopItem. All size fields are in bytes. The channel is closed when Phase == PhaseComplete or when an error occurs (Err != nil).

type SteamGuardCallback

type SteamGuardCallback func() (string, error)

SteamGuardCallback is called when Steam requires a Guard code during login. The callback should return the 5-character alphanumeric code, or an error. It is invoked for both email-based and TOTP-based Steam Guard.

func InteractiveSteamGuard

func InteractiveSteamGuard() SteamGuardCallback

InteractiveSteamGuard returns a SteamGuardCallback that prompts the user to type the Steam Guard code on standard input. Works for both email and TOTP guard when running interactively in a terminal.

func SteamGuardCodeGenerate

func SteamGuardCodeGenerate(sharedSecret string) SteamGuardCallback

SteamGuardCodeGenerate returns a SteamGuardCallback that automatically generates a Steam Guard code from a TOTP shared secret (mobile authenticator). sharedSecret is the base64-encoded secret shown during authenticator setup.

func UnknownSteamGuard

func UnknownSteamGuard() SteamGuardCallback

UnknownSteamGuard returns a SteamGuardCallback that always returns an error. This is the default when no SteamGuardCallback is provided in Config, so that Steam Guard failures produce a clear message rather than hanging forever.

type WorkshopDownloadRequest

type WorkshopDownloadRequest struct {
	// ItemID is the PublishedFileID (workshop item ID).
	ItemID uint64

	// TargetDir is the directory where workshop files will be written.
	TargetDir string
}

WorkshopDownloadRequest describes a Steam Workshop item download.

Directories

Path Synopsis
cmd
app-download command
guard command
internal
cdn
Package cdn implements the Steam Content Delivery Network (CDN) client for downloading depot manifests and content chunks.
Package cdn implements the Steam Content Delivery Network (CDN) client for downloading depot manifests and content chunks.
cm
Package cm implements the Steam CM (Connection Manager) TCP protocol, including the AES encryption handshake, proto message framing, heartbeat, and automatic reconnect with exponential back-off.
Package cm implements the Steam CM (Connection Manager) TCP protocol, including the AES encryption handshake, proto message framing, heartbeat, and automatic reconnect with exponential back-off.
proto
Package proto contains Steam network protocol message types and wire encoding.
Package proto contains Steam network protocol message types and wire encoding.
workshop
Package workshop resolves Steam Workshop item metadata via the Web API.
Package workshop resolves Steam Workshop item metadata via the Web API.
Package mock provides in-memory test doubles for go-steam internals.
Package mock provides in-memory test doubles for go-steam internals.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL