cotlib

package module
v0.2.4 Latest Latest
Warning

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

Go to latest
Published: May 12, 2025 License: MIT Imports: 11 Imported by: 0

README

CoT Library

A Go library for working with Cursor-on-Target (CoT) messages.

Features

  • Full CoT type catalog with metadata
  • Type validation and registration
  • Secure logging with slog
  • Thread-safe catalog system
  • Wildcard pattern support
  • Search by description or full name

Installation

go get github.com/NERVsystems/cotlib

Usage

Basic Event Creation
package main

import (
    "log/slog"
    "os"
    "github.com/NERVsystems/cotlib"
)

func main() {
    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
    
    // Register a custom CoT type
    if err := cotlib.RegisterCoTType("a-f-G-U-C-F"); err != nil {
        logger.Error("Failed to register type", "error", err)
        return
    }
    
    // Validate a CoT type
    if valid := cotlib.ValidateType("a-f-G-U-C-F"); valid {
        logger.Info("Type is valid")
    }
}
Type Catalog Operations
package main

import (
    "fmt"
    "log"
    "github.com/NERVsystems/cotlib"
)

func main() {
    // Look up type metadata
    fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Full name: %s\n", fullName)
    // Output: Full name: Gnd/Equip/Nbc Equipment

    // Get type description
    desc, err := cotlib.GetTypeDescription("a-f-G-E-X-N")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Description: %s\n", desc)
    // Output: Description: NBC EQUIPMENT

    // Search for types by description
    types := cotlib.FindTypesByDescription("NBC")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
    }

    // Search for types by full name
    types = cotlib.FindTypesByFullName("Equipment")
    for _, t := range types {
        fmt.Printf("Found type: %s (%s)\n", t.Name, t.FullName)
    }
}
Thread Safety

All operations in the library are thread-safe. The type catalog uses internal synchronization to ensure safe concurrent access.

Type Validation

The library enforces strict validation of CoT types:

  • Basic syntax checking
  • Standard prefix validation
  • Length limits
  • Wildcard pattern validation
Custom Types

You can register custom type codes that extend the standard prefixes:

// Register a custom type
cotlib.RegisterCoTType("a-f-G-E-V-custom")

// Validate the custom type
if err := cotlib.ValidateType("a-f-G-E-V-custom"); err != nil {
    log.Fatal(err)
}
Logging

The library uses slog for structured logging:

  • Debug level for detailed operations
  • Info level for normal events
  • Warn level for recoverable issues
  • Error level for critical problems
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
    Level: slog.LevelDebug,
}))

ctx := cotlib.WithLogger(context.Background(), logger)

Documentation

For detailed documentation and examples, see:

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Documentation

Overview

Package cotlib implements the Cursor on Target (CoT) protocol for Go.

The package provides data structures and utilities for parsing and generating CoT messages, as well as a comprehensive type catalog system for working with CoT type codes.

Type Catalog

The type catalog system provides a way to work with CoT type codes and their metadata. Each type code (e.g., "a-f-G-E-X-N") has associated metadata:

  • Full Name: A hierarchical name (e.g., "Gnd/Equip/Nbc Equipment")
  • Description: A human-readable description (e.g., "NBC EQUIPMENT")

The catalog supports several operations:

  • Looking up metadata for a specific type code
  • Searching for types by description or full name
  • Validating type codes
  • Registering custom type codes

Example usage:

// Look up type metadata
fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Full name: %s\n", fullName)

// Search for types
types := cotlib.FindTypesByDescription("NBC")
for _, t := range types {
    fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
}

Thread Safety

All operations on the type catalog are thread-safe. The catalog uses internal synchronization to ensure safe concurrent access.

Custom Types

Applications can register custom type codes using RegisterCoTType. These custom types must follow the standard CoT type format and will be validated before registration.

For more information about CoT types and their format, see: https://www.mitre.org/sites/default/files/pdf/09_4937.pdf

Security features include:

  • XML parsing restrictions to prevent XXE attacks
  • Input validation on all fields
  • Strict coordinate range enforcement
  • Time field validation to prevent time-based attacks
  • Secure logging practices
  • Detail extension isolation

For more information about CoT, see:

The package follows these design principles:

  • High cohesion: focused on CoT event parsing and serialization
  • Low coupling: separated concerns for expansions and transport
  • Composition over inheritance: nested sub-structures for detail fields
  • Full schema coverage: implements Event.xsd with example extensions
  • Secure by design: validates inputs and prevents common attacks
Example (TypePredicates)

Example_typePredicates demonstrates using type predicates

package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create some example events
	events := []*cotlib.Event{
		{Type: "a-f-G-U-C"}, // Friendly ground combat unit
		{Type: "a-h-A-M-F"}, // Hostile fixed wing aircraft
		{Type: "b-d-c-n-r"}, // NBC radiation detection
		{Type: "t-s-i-e"},   // ISR EO tasking
	}

	// Test various predicates
	predicates := []string{"atom", "friend", "hostile", "ground", "air"}

	for _, evt := range events {
		fmt.Printf("\nEvent type: %s\n", evt.Type)
		for _, pred := range predicates {
			if evt.Is(pred) {
				fmt.Printf("  Matches predicate: %s\n", pred)
			}
		}
	}

}
Output:

Event type: a-f-G-U-C
  Matches predicate: atom
  Matches predicate: friend
  Matches predicate: ground

Event type: a-h-A-M-F
  Matches predicate: atom
  Matches predicate: hostile
  Matches predicate: air

Event type: b-d-c-n-r
  Matches predicate: atom

Event type: t-s-i-e
  Matches predicate: atom

Index

Examples

Constants

View Source
const (

	// CotTimeFormat is the standard time format for CoT messages (Zulu time, no offset)
	// Format: "2006-01-02T15:04:05Z" (UTC without timezone offset)
	CotTimeFormat = "2006-01-02T15:04:05Z"
)

Security limits for XML parsing and validation

Variables

View Source
var (
	ErrInvalidInput    = fmt.Errorf("invalid input")
	ErrInvalidLatitude = fmt.Errorf("invalid latitude")
	ErrInvalidUID      = fmt.Errorf("invalid UID")
)

Error sentinels for validation

Functions

func FindTypes

func FindTypes(query string) []cottypes.Type

FindTypes returns all types matching the given query

func FindTypesByDescription

func FindTypesByDescription(desc string) []cottypes.Type

FindTypesByDescription searches for types matching the given description. The search is case-insensitive and matches partial descriptions.

For example:

  • "NBC" finds all types containing "NBC" in their description
  • "EQUIPMENT" finds all equipment-related types
  • "COMBAT" finds all combat-related types

This is useful for building search interfaces and type discovery tools. Returns an empty slice if no matches are found.

Example
package main

import (
	"fmt"
	"sort"

	"github.com/NERVsystems/cotlib"
)

func main() {
	types := cotlib.FindTypesByDescription("NBC EQUIPMENT")
	// Sort by name for consistent output
	sort.Slice(types, func(i, j int) bool {
		return types[i].Name < types[j].Name
	})
	for _, t := range types {
		fmt.Printf("Found type: %s (%s)\n", t.Name, t.Description)
	}
}
Output:

Found type: a-f-G-E-X-N (NBC EQUIPMENT)
Found type: a-h-G-E-X-N (NBC EQUIPMENT)
Found type: a-n-G-E-X-N (NBC EQUIPMENT)
Found type: a-u-G-E-X-N (NBC EQUIPMENT)

func FindTypesByFullName

func FindTypesByFullName(name string) []cottypes.Type

FindTypesByFullName searches for types matching the given full name. The search is case-insensitive and matches partial names.

For example:

  • "Nbc Equipment" finds all NBC equipment types
  • "Ground" finds all ground-based types
  • "Vehicle" finds all vehicle types

This is useful for finding types based on their hierarchical classification. Returns an empty slice if no matches are found.

Example
package main

import (
	"fmt"
	"sort"

	"github.com/NERVsystems/cotlib"
)

func main() {
	types := cotlib.FindTypesByFullName("Gnd/Equip/Nbc Equipment")
	// Sort by name for consistent output
	sort.Slice(types, func(i, j int) bool {
		return types[i].Name < types[j].Name
	})
	for _, t := range types {
		fmt.Printf("Found type: %s (%s)\n", t.Name, t.FullName)
	}
}
Output:

Found type: a-f-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-h-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-n-G-E-X-N (Gnd/Equip/Nbc Equipment)
Found type: a-u-G-E-X-N (Gnd/Equip/Nbc Equipment)

func GetTypeDescription

func GetTypeDescription(name string) (string, error)

GetTypeDescription returns the human-readable description for a CoT type. For example, "a-f-G-E-X-N" returns "NBC EQUIPMENT".

The description is a concise explanation of what the type represents, suitable for display in user interfaces and logs.

Returns an error if the type is not registered in the catalog.

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	desc, err := cotlib.GetTypeDescription("a-f-G-E-X-N")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("Description: %s\n", desc)
}
Output:

Description: NBC EQUIPMENT

func GetTypeFullName

func GetTypeFullName(name string) (string, error)

GetTypeFullName returns the full hierarchical name for a CoT type. For example, "a-f-G-E-X-N" returns "Gnd/Equip/Nbc Equipment".

The full name represents the type's position in the CoT type hierarchy, making it useful for building user interfaces and documentation.

Returns an error if the type is not registered in the catalog.

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	fullName, err := cotlib.GetTypeFullName("a-f-G-E-X-N")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}
	fmt.Printf("Full name: %s\n", fullName)
}
Output:

Full name: Gnd/Equip/Nbc Equipment

func LoadCoTTypesFromFile

func LoadCoTTypesFromFile(path string) error

LoadCoTTypesFromFile loads CoT types from a file

func LookupType

func LookupType(name string) (cottypes.Type, bool)

LookupType returns the Type for the given name if it exists

func RegisterAllCoTTypes

func RegisterAllCoTTypes() error

RegisterAllCoTTypes is a no-op since XML is already embedded

func RegisterCoTType

func RegisterCoTType(name string)

RegisterCoTType adds a specific CoT type to the valid types registry It does not log individual type registrations to avoid log spam

func RegisterCoTTypesFromFile

func RegisterCoTTypesFromFile(filename string) error

RegisterCoTTypesFromFile loads and registers CoT types from an XML file

func RegisterCoTTypesFromReader

func RegisterCoTTypesFromReader(r io.Reader) error

RegisterCoTTypesFromReader loads and registers CoT types from an XML reader

func RegisterCoTTypesFromXMLContent

func RegisterCoTTypesFromXMLContent(xmlContent string) error

RegisterCoTTypesFromXMLContent registers CoT types from the given XML content string This is particularly useful for embedding the CoTtypes.xml content directly in code

func SetLogger

func SetLogger(l *slog.Logger)

SetLogger sets the package-level logger

func SetMaxValueLen

func SetMaxValueLen(max int64)

SetMaxValueLen sets the maximum allowed length for XML attribute values and character data This is used to prevent memory exhaustion attacks via large XML payloads

func ValidateLatLon

func ValidateLatLon(lat, lon float64) error

ValidateLatLon checks if latitude and longitude are within valid ranges

func ValidateType

func ValidateType(typ string) error

ValidateType checks if a CoT type is valid

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Test various CoT types
	types := []string{
		"a-f-G",      // Friendly ground
		"a-h-A",      // Hostile air
		"b-d",        // Detection
		"t-x-takp-v", // TAK presence
		"invalid",    // Invalid type
	}

	for _, typ := range types {
		err := cotlib.ValidateType(typ)
		fmt.Printf("Type %s: %v\n", typ, err == nil)
	}

}
Output:

Type a-f-G: true
Type a-h-A: true
Type b-d: true
Type t-x-takp-v: true
Type invalid: false

func ValidateUID

func ValidateUID(uid string) error

ValidateUID checks if a UID is valid

func WithLogger

func WithLogger(ctx context.Context, l *slog.Logger) context.Context

WithLogger adds a logger to the context

Types

type CoTTime

type CoTTime time.Time

CoTTime represents a time in CoT format (UTC without timezone offset)

func (CoTTime) MarshalXML

func (t CoTTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML implements xml.Marshaler

func (CoTTime) MarshalXMLAttr

func (t CoTTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error)

MarshalXMLAttr implements xml.MarshalerAttr

func (CoTTime) Time

func (t CoTTime) Time() time.Time

Time returns the underlying time.Time value

func (*CoTTime) UnmarshalXML

func (t *CoTTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error

UnmarshalXML implements xml.Unmarshaler

func (*CoTTime) UnmarshalXMLAttr

func (t *CoTTime) UnmarshalXMLAttr(attr xml.Attr) error

UnmarshalXMLAttr implements xml.UnmarshalerAttr

type Contact

type Contact struct {
	Callsign string `xml:"callsign,attr,omitempty"`
}

Contact represents contact information

type Detail

type Detail struct {
	Group   *Group   `xml:"group,omitempty"`
	Contact *Contact `xml:"contact,omitempty"`
}

Detail contains additional information about an event

type Event

type Event struct {
	XMLName xml.Name `xml:"event"`
	Version string   `xml:"version,attr"`
	Uid     string   `xml:"uid,attr"`
	Type    string   `xml:"type,attr"`
	How     string   `xml:"how,attr,omitempty"`
	Time    CoTTime  `xml:"time,attr"`
	Start   CoTTime  `xml:"start,attr"`
	Stale   CoTTime  `xml:"stale,attr"`
	Point   Point    `xml:"point"`
	Detail  *Detail  `xml:"detail,omitempty"`
	Links   []Link   `xml:"link,omitempty"`
}

Event represents a CoT event message

func NewEvent

func NewEvent(uid, typ string, lat, lon, hae float64) (*Event, error)

NewEvent creates a new CoT event with the given parameters

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a new event with a friendly ground unit
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Add some details
	event.Detail = &cotlib.Detail{
		Contact: &cotlib.Contact{
			Callsign: "TEST-1",
		},
	}

	// Print event details
	fmt.Printf("Event Type: %s\n", event.Type)
	fmt.Printf("Location: %.2f, %.2f\n", event.Point.Lat, event.Point.Lon)
	fmt.Printf("Callsign: %s\n", event.Detail.Contact.Callsign)

}
Output:

Event Type: a-f-G
Location: 30.00, -85.00
Callsign: TEST-1

func NewPresenceEvent

func NewPresenceEvent(uid string, lat, lon, hae float64) (*Event, error)

NewPresenceEvent creates a new presence event (t-x-takp-v)

func UnmarshalXMLEvent

func UnmarshalXMLEvent(data []byte) (*Event, error)

UnmarshalXMLEvent parses an XML byte slice into an Event

func (e *Event) AddLink(link *Link)

AddLink adds a link to the event

func (*Event) InjectIdentity

func (e *Event) InjectIdentity(selfUid, groupName, groupRole string)

InjectIdentity adds identity information to the event

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a new event
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Inject identity information
	event.InjectIdentity("self123", "Blue", "HQ")

	// Print identity details
	if event.Detail != nil && event.Detail.Group != nil {
		fmt.Printf("Group: %s\n", event.Detail.Group.Name)
		fmt.Printf("Role: %s\n", event.Detail.Group.Role)
	}

}
Output:

Group: Blue
Role: HQ

func (*Event) Is

func (e *Event) Is(pred string) bool

Is checks if the event matches a predicate

Example
package main

import (
	"fmt"

	"github.com/NERVsystems/cotlib"
)

func main() {
	// Create a friendly ground unit event
	event, err := cotlib.NewEvent("test123", "a-f-G", 30.0, -85.0, 0.0)
	if err != nil {
		fmt.Printf("Error creating event: %v\n", err)
		return
	}

	// Check various predicates
	fmt.Printf("Is friendly: %v\n", event.Is("friend"))
	fmt.Printf("Is hostile: %v\n", event.Is("hostile"))
	fmt.Printf("Is ground: %v\n", event.Is("ground"))
	fmt.Printf("Is air: %v\n", event.Is("air"))

}
Output:

Is friendly: true
Is hostile: false
Is ground: true
Is air: false

func (*Event) ToXML

func (e *Event) ToXML() ([]byte, error)

ToXML converts an Event to XML bytes

func (*Event) Validate

func (e *Event) Validate() error

Validate checks if the event is valid

type Group

type Group struct {
	Name string `xml:"name,attr"`
	Role string `xml:"role,attr"`
}

Group represents a group affiliation

type Link struct {
	Uid      string `xml:"uid,attr"`
	Type     string `xml:"type,attr"`
	Relation string `xml:"relation,attr"`
}

Link represents a relationship to another event

type Point

type Point struct {
	Lat float64 `xml:"lat,attr"` // Latitude in degrees
	Lon float64 `xml:"lon,attr"` // Longitude in degrees
	Hae float64 `xml:"hae,attr"` // Height above ellipsoid in meters
	Ce  float64 `xml:"ce,attr"`  // Circular error in meters
	Le  float64 `xml:"le,attr"`  // Linear error in meters
}

Point represents a location in 3D space with error estimates

func (*Point) Validate

func (p *Point) Validate() error

Validate checks if the point coordinates and errors are valid

Directories

Path Synopsis
cmd
cotgen command
logcheck command

Jump to

Keyboard shortcuts

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