RepoPilotOpen in app →

go-yaml/yaml

YAML support for the Go language.

Mixed

Stale — last commit 1y ago

weakest axis
Use as dependencyConcerns

non-standard license (Other); last commit was 1y ago…

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 14 active contributors
  • Other licensed
  • CI configured
Show all 7 evidence items →
  • Stale — last commit 1y ago
  • Single-maintainer risk — top contributor 82% of recent commits
  • Non-standard license (Other) — review terms
  • No test directory detected
What would change the summary?
  • Use as dependency ConcernsMixed if: clarify license terms

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/go-yaml/yaml?axis=fork)](https://repopilot.app/r/go-yaml/yaml)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/go-yaml/yaml on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: go-yaml/yaml

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/go-yaml/yaml shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Stale — last commit 1y ago

  • 14 active contributors
  • Other licensed
  • CI configured
  • ⚠ Stale — last commit 1y ago
  • ⚠ Single-maintainer risk — top contributor 82% of recent commits
  • ⚠ Non-standard license (Other) — review terms
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live go-yaml/yaml repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/go-yaml/yaml.

What it runs against: a local clone of go-yaml/yaml — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in go-yaml/yaml | Confirms the artifact applies here, not a fork | | 2 | License is still Other | Catches relicense before you depend on it | | 3 | Default branch v3 exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 431 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>go-yaml/yaml</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of go-yaml/yaml. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/go-yaml/yaml.git
#   cd yaml
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of go-yaml/yaml and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "go-yaml/yaml(\\.git)?\\b" \\
  && ok "origin remote is go-yaml/yaml" \\
  || miss "origin remote is not go-yaml/yaml (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
  && ok "license is Other" \\
  || miss "license drift — was Other at generation time"

# 3. Default branch
git rev-parse --verify v3 >/dev/null 2>&1 \\
  && ok "default branch v3 exists" \\
  || miss "default branch v3 no longer exists"

# 4. Critical files exist
test -f "yaml.go" \\
  && ok "yaml.go" \\
  || miss "missing critical file: yaml.go"
test -f "decode.go" \\
  && ok "decode.go" \\
  || miss "missing critical file: decode.go"
test -f "encode.go" \\
  && ok "encode.go" \\
  || miss "missing critical file: encode.go"
test -f "scannerc.go" \\
  && ok "scannerc.go" \\
  || miss "missing critical file: scannerc.go"
test -f "parserc.go" \\
  && ok "parserc.go" \\
  || miss "missing critical file: parserc.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 431 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~401d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/go-yaml/yaml"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

go-yaml/yaml is a pure Go port of libyaml that encodes and decodes YAML 1.2 data (with YAML 1.1 backwards compatibility). It provides the gopkg.in/yaml.v3 package for Go programs to marshal/unmarshal YAML structures into Go types, supporting anchors, tags, map merging, and multi-document parsing without external C dependencies. Flat monolith: core parsing/emitting logic in scannerc.go, parserc.go, emitterc.go (C library ports), with public API in yaml.go. Encode path flows through encode.go, decode path through decode.go. Node-based AST parsing in apic.go. Utility files (resolve.go, sorter.go, readerc.go, writerc.go) handle tag resolution and writer management. Tests colocated (decode_test.go, encode_test.go) alongside implementation.

👥Who it's for

Go developers building infrastructure tools, configuration parsers, and data serialization systems (originally created at Canonical for the Juju project). Primarily used by ops engineers and DevOps tooling authors who need reliable YAML parsing without cgo or external dependencies.

🌱Maturity & risk

Production-ready but unmaintained as of the README notice. The codebase is ~455KB of Go across 20+ core files with comprehensive test coverage (decode_test.go, encode_test.go, node_test.go), CI via GitHub Actions (.github/workflows/go.yaml), and stable v3 API. However, the original maintainer (Gustavo Niemeyer) explicitly marked it unmaintained in 2023+ due to lack of personal capacity, making it a mature-but-stalled library.

Moderate risk for new code: only 1 external dependency (gopkg.in/check.v1 for testing), but unmaintained status means security fixes and breaking YAML spec updates won't land. The single-maintainer handoff failed, leaving no clear upgrade path. Safe for existing applications but risky for greenfield projects requiring long-term support or YAML 1.2 spec compliance guarantees.

Active areas of work

No active development. The README explicitly states this is unmaintained. No recent commits, PRs, or milestones are visible. The last activity was labeling the project as unmaintained; no roadmap for future work exists.

🚀Get running

Clone the repo: git clone https://github.com/go-yaml/yaml.git && cd yaml. No build step needed for library use. To run tests: go test ./... (requires Go 1.11+). To use in your project: go get gopkg.in/yaml.v3.

Daily commands: This is a library, not a runnable service. To test: go test ./... -v. To use in code: import "gopkg.in/yaml.v3" and call yaml.Unmarshal(data, &target) or yaml.Marshal(source). Example files (example_embedded_test.go) show basic usage patterns.

🗺️Map of the codebase

  • yaml.go — Main entry point exposing Marshal/Unmarshal APIs; essential for understanding the public interface.
  • decode.go — Core YAML parsing and deserialization logic; handles conversion from YAML tokens to Go values.
  • encode.go — Core YAML serialization logic; handles conversion from Go values back to YAML format.
  • scannerc.go — Lexical scanner that tokenizes YAML input; foundation for all parsing operations.
  • parserc.go — YAML parser that converts token stream into node structure; critical abstraction layer.
  • emitterc.go — Converts internal node representation to YAML text output; key encoding stage.
  • resolve.go — Type resolution and YAML tag handling; determines Go types during deserialization.

🧩Components & responsibilities

  • scannerc.go (Scanner) (Byte-level state machine, indentation tracking) — Tokenizes raw YAML input into events (scalar, flow start, block start, etc.)
    • Failure mode: Malformed token stream on invalid YAML syntax; cascading parser errors
  • parserc.go (Parser) (Recursive descent, stack-based node construction) — Builds node tree from token stream; enforces YAML syntax rules
    • Failure mode: Unbalanced structures, type mismatches; produces invalid node tree
  • resolve.go (Type Resolver) (Tag parsing, regex matching, type assertions) — Determines Go type from YAML tag and infers type from value content
    • Failure mode: Wrong type inference; data loss or conversion errors
  • decode.go (Decoder) (Go reflect, custom Unmarshaler interface) — Converts node tree to Go values via reflection; handles pointers, interfaces, custom types
    • Failure mode: Panics on type mismatches; nil pointer dereferences; incomplete struct population
  • encode.go (Encoder) (Go reflect, custom Marshaler interface, sorter for determinism) — Converts Go values to node tree via reflection; detects custom Marshaler implementations
    • Failure mode: Infinite recursion on circular references; panic on unsupported types
  • emitterc.go (Emitter) (String builder, indentation tracking, quote detection) — Renders node tree to YAML text with proper formatting, quoting, and escaping
    • Failure mode: Invalid YAML output if quoting logic fails; data corruption

🔀Data flow

  • User input (YAML bytes)readerc.go — Raw YAML text enters the pipeline
  • readerc.goscannerc.go — Reader buffers bytes; scanner tokenizes into events
  • scannerc.goparserc.go — Token stream structured into node tree
  • parserc.goresolve.go — Nodes analyzed for YAML tag and type inference
  • resolve.godecode.go — Type information guides value assignment
  • decode.goUser target (Go value) — Decoded struct/map/slice populated and returned
  • User value (Go struct)encode.go — undefined

🛠️How to make changes

Add support for a new YAML tag type

  1. Define the custom Go type struct in your code or yamlprivateh.go (yamlprivateh.go)
  2. Add tag resolution logic to map YAML tag to Go type (resolve.go)
  3. Add unmarshaling logic in the decoder to instantiate your type (decode.go)
  4. Add marshaling logic in the encoder to serialize your type (encode.go)
  5. Add test cases covering both encode and decode paths (encode_test.go)

Implement a custom YAML marshaler for a struct

  1. Implement yaml.Marshaler interface (check yaml.go for interface definition) (yaml.go)
  2. Encoder automatically detects and calls MarshalYAML() method (encode.go)
  3. Add tests validating custom marshaling behavior (encode_test.go)

Fix a YAML parsing issue

  1. Add minimal failing test case to decode_test.go (decode_test.go)
  2. Identify if issue is in lexical phase (token generation) (scannerc.go)
  3. Or identify if issue is in syntax phase (node structure) (parserc.go)
  4. Or identify if issue is in semantic phase (type conversion) (resolve.go)
  5. Or identify if issue is in decoding phase (value assignment) (decode.go)

🔧Why these technologies

  • Go — Official YAML library for Go; pure Go implementation with no C dependencies enables cross-platform compilation.
  • Recursive descent parsing — Suitable for YAML's context-sensitive indentation-based syntax; allows precise error reporting.
  • Node-based intermediate representation — Decouples YAML structure from Go types; enables flexible marshaling and unmarshaling without multiple passes.

⚖️Trade-offs already made

  • Manual recursive descent parser instead of generated parser

    • Why: YAML syntax is complex and context-sensitive; hard to express in BNF. Manual implementation gives fine-grained control.
    • Consequence: More code to maintain but better error messages and flexibility; harder to reason about correctness.
  • Two-phase decoding: YAML → nodes → Go values

    • Why: Separates concerns and allows preserving YAML structure for Node type users
    • Consequence: Slight performance overhead vs direct YAML→Go conversion, but enables advanced use cases.
  • No external C library dependencies

    • Why: Pure Go enables simple deployment and cross-compilation
    • Consequence: Cannot leverage battle-tested C parsers like libyaml; own implementation may have undiscovered edge cases.
  • Unmaintained project with no long-term support

    • Why: Creator lacks bandwidth; unable to hand off to community
    • Consequence: Security and compatibility issues may accumulate; users should consider maintained alternatives.

🚫Non-goals (don't propose these)

  • Full YAML 1.2 spec compliance (uses YAML 1.1 semantics by design)
  • Streaming/incremental parsing of large YAML documents
  • Real-time validation with custom validators
  • JSON-to-YAML bidirectional seamless conversion
  • Active maintenance and bug fixes

🪤Traps & gotchas

No major hidden configuration. Key gotchas: (1) YAML 1.1 bool compat: yes/no/on/off only work when decoding into typed bool; otherwise treated as strings. (2) Octal format: uses 0777 (YAML 1.1) not 0o777 (YAML 1.2) by default for backwards compat. (3) No base-60 floats (intentionally omitted). (4) Multi-document unmarshaling not yet implemented (see README). (5) Unmaintained status means spec-compliance bugs may never be fixed.

🏗️Architecture

💡Concepts to learn

  • Recursive Descent Parsing — Core technique used in parserc.go to convert YAML token stream into structured AST; essential to understanding how YAML syntax is validated
  • Abstract Syntax Tree (AST) / Node Representation — go-yaml represents parsed YAML as a Node tree (apic.go) before converting to Go values; low-level API access requires understanding this intermediate representation
  • YAML Anchors & Aliases — go-yaml supports YAML 1.2 anchors (&name) and aliases (*name) for deduplication; critical for parsing complex nested configs without understanding this breaks round-trip fidelity
  • Tag Resolution (Implicit Type Inference) — resolve.go handles YAML's implicit typing (inferring int/float/bool/string from untagged values); essential for understanding why yes might parse as bool or string depending on context
  • libyaml C-to-Go Port Pattern — scannerc.go and parserc.go are direct Go translations of C library logic; understanding this 1:1 mapping helps when debugging spec compliance against original libyaml
  • Struct Tag Marshaling (Go Reflection) — encode.go and decode.go use Go's reflect package to read struct tags (yaml:"-", yaml:"field,omitempty") for custom marshaling; essential for configuring encoding behavior
  • satori/go.uuid — Canonical package for UUID handling in Go; often used alongside YAML for generating/parsing IDs in config files
  • kubernetes/kubernetes — Massive consumer of go-yaml/yaml for parsing Kubernetes manifests and config; key real-world validation of this library
  • hashicorp/hcl — Alternative config language parser for Go; comparable use case but HCL syntax instead of YAML
  • pyyaml/pyyaml — Original libyaml inspiration and Python reference implementation; go-yaml is a pure Go port of libyaml, not pyyaml
  • go-cto/go-yaml — Modern maintained YAML v3 fork/alternative for Go if upstream abandonment becomes critical

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive edge case tests for scannerc.go and parserc.go

The scanner and parser are core components handling YAML tokenization and parsing. While decode_test.go and encode_test.go exist, there are no dedicated unit tests for scannerc.go and parserc.go. Given the project is unmaintained and these files contain complex state machines, adding focused tests for edge cases (malformed input, deeply nested structures, special character handling) would improve code resilience and help future maintainers understand failure modes.

  • [ ] Create scanner_test.go with tests for boundary conditions: empty input, invalid UTF-8, extremely long tokens, and unclosed quotes
  • [ ] Create parser_test.go with tests for malformed YAML structures: mismatched indentation, invalid anchors/aliases, circular references
  • [ ] Add fuzz testing inputs to catch panics in scannerc.go and parserc.go tokenization logic
  • [ ] Reference existing test patterns from decode_test.go and encode_test.go for consistency

Add resolver_test.go for type resolution and tagging logic in resolve.go

resolve.go handles YAML tag resolution and implicit type inference, which is critical for correct data parsing. Currently, there is no dedicated test file for this module. Type resolution edge cases (custom tags, null handling, numeric type ambiguities) need explicit test coverage to prevent regressions as the project accepts external contributions.

  • [ ] Create resolver_test.go covering implicit tag resolution for: scalars, numbers, booleans, nulls, and custom tags
  • [ ] Test edge cases: ambiguous numeric formats (octal, hex, scientific notation), empty strings vs nulls, and unicode handling
  • [ ] Verify resolveTag() and Resolve() functions with both standard and non-standard YAML tag URIs
  • [ ] Add regression tests for common type inference gotchas documented in YAML 1.2 spec

Expand .github/workflows/go.yaml with multi-version testing and security scanning

The existing go.yaml workflow is minimal and doesn't test against multiple Go versions or run security checks. Given the project's unmaintained status and reliance on community contributions, a robust CI pipeline is essential. Add Go 1.19, 1.20, 1.21 testing and basic security scanning to catch version compatibility issues and common vulnerabilities before merging contributions.

  • [ ] Update .github/workflows/go.yaml to test against Go 1.19, 1.20, 1.21 (and latest stable) using matrix strategy
  • [ ] Add golangci-lint step to check for code quality issues, unused code, and common Go mistakes
  • [ ] Add go vet and go fmt verification steps to enforce code style
  • [ ] Optional: integrate with gosec or trivy for security vulnerability scanning of dependencies
  • [ ] Test on multiple platforms (ubuntu, windows, macos) if resource-constrained, at least ubuntu and windows

🌿Good first issues

  • Add explicit tests for YAML 1.1 octal edge cases (0777 vs 0o777 parsing) in decode_test.go to clarify current behavior and document the backwards-compat choice
  • Extend node_test.go with examples of all Node.Tag types (!!str, !!int, !!map, etc.) and their round-trip encode/decode behavior to improve AST documentation
  • Add a practical example in example_embedded_test.go showing multi-document workaround patterns (yaml.Unmarshal on split inputs) since multi-doc is documented as 'not yet implemented'

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 944c86a — Update README.md. (niemeyer)
  • f6f7691 — The Billion Dollar Mistake (niemeyer)
  • 00bbc09 — github: drop broken semgrep workflow (niemeyer)
  • 8f96da9 — Explicitly check the parser for errors on peek (niemeyer)
  • 539c8e7 — Fix decoding of merge tags. (niemeyer)
  • 496545a — Improve inline comments on map keys (#656). (niemeyer)
  • 749611f — Allow encoding Node by value (#673). (niemeyer)
  • c8046fb — Improve the behavior of null decoding (#681). (niemeyer)
  • c476de3 — Tune previous change so it fixes #686 too. (niemeyer)
  • 3778e11 — Fix bug addressed by Felix Fontein in #684. (niemeyer)

🔒Security observations

The go-yaml/yaml library presents significant security concerns primarily due to its unmaintained status. While no obvious code-level vulnerabilities (hardcoded secrets, injection flaws, misconfigurations) are present in the file structure analysis, the lack of active maintenance is the primary security risk. Dependencies are outdated, and security patches for any future vulnerabilities cannot be guaranteed. Users should carefully evaluate whether to use this library and consider alternatives or maintain their own fork if critical security patches are needed.

  • High · Unmaintained Project with Known Security Risks — README.md, Project Status. The project is explicitly labeled as unmaintained in the README. This means security vulnerabilities discovered may not be patched in a timely manner. The maintainer acknowledges lack of free time and no active engagement from long-term contributors, creating a significant risk for users depending on this library. Fix: Consider migrating to a maintained YAML library or fork the project with active maintenance. Monitor for security advisories and apply patches independently if necessary.
  • Medium · Outdated Testing Dependency — go.mod. The project uses 'gopkg.in/check.v1' pinned to version v0.0.0-20161208181325-20d25e280405, which is from 2016. This is an extremely old version that may contain known vulnerabilities or lack modern security features. Fix: Update 'gopkg.in/check.v1' to the latest stable version. Run 'go get -u gopkg.in/check.v1' and test thoroughly to ensure compatibility.
  • Medium · YAML Parsing Vulnerability Risk — decode.go, parserc.go, scannerc.go. YAML parsers are known to be targets for deserialization attacks (e.g., arbitrary code execution through unsafe object instantiation). While Go's implementation is generally safer than Python or Ruby, the unmaintained status increases the risk that newly discovered YAML parsing vulnerabilities may not be addressed. Fix: Validate and sanitize all YAML input. Avoid parsing untrusted YAML data. Implement input size limits and complexity restrictions. Monitor security advisories for YAML parsing vulnerabilities.
  • Low · No Security Policy or Vulnerability Reporting Mechanism — .github/, Project Configuration. The project does not appear to have a SECURITY.md file or documented vulnerability disclosure process. For an unmaintained project, this makes it difficult for security researchers to report issues responsibly. Fix: Although the project is unmaintained, consider creating a SECURITY.md file with clear guidance on how vulnerabilities should be reported to the original maintainer or community forks.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · go-yaml/yaml — RepoPilot