Documentation
¶
Overview ¶
Package cap reads, checks, writes and signs OASIS Common Alerting Protocol 1.2 alert messages, and reads and writes the Atom and RSS feeds that carry them.
CAP 1.1 and 1.0 alerts (and alerts without a namespace) are read as CAP 1.2, and Validate lists every problem rather than stopping at the first, so an application can store an alert that strays from the schema with its problems noted (Problem.Essential tells which problems leave an alert unusable). Signatures are enveloped XML signatures over the whole alert, verified against certificates the application pins (sign.go). Areas convert to GeoJSON (geo.go).
The message model follows the CAP 1.2 schema; no schema or specification text is copied here.
Example ¶
A CAP 1.1 alert is read as 1.2; Validate lists every problem, and Essential tells whether the alert can still be kept.
package main
import (
"fmt"
"github.com/cB-Sigma/cap"
)
func main() {
doc := []byte(`<alert xmlns="urn:oasis:names:tc:emergency:cap:1.1">
<identifier>DEMO-1</identifier><sender>demo@example.test</sender>
<sent>2026-09-24T10:00:00Z</sent><status>Actual</status><msgType>Alert</msgType><scope>Public</scope>
<info><category>Met</category><event>Heavy Rainfall</event><urgency>Expected</urgency>
<severity>Severe</severity><certainty>Likely</certainty>
<area><areaDesc>Demo district</areaDesc><circle>18.52,73.85 5</circle></area></info>
</alert>`)
a, err := cap.Parse(doc)
if err != nil {
panic(err)
}
fmt.Println(a.Version, a.Identifier, a.MostSevere())
if err := a.Validate(cap.Options{}); err != nil {
fmt.Println(err, "- essential:", err.(cap.ValidationError).Essential())
}
fmt.Println(a.Validate(cap.Options{LenientTime: true}) == nil)
}
Output: 1.1 DEMO-1 Severe the CAP alert is not valid: sent: "2026-09-24T10:00:00Z" is not in the form 2026-09-24T10:00:00+05:30 - essential: true true
Index ¶
- Constants
- Variables
- func CirclePolygon(center []float64, radiusKm float64) [][]float64
- func FormatTime(t time.Time) string
- func ParseCircle(s string) (center []float64, radiusKm float64, err error)
- func ParsePolygon(s string) ([][]float64, error)
- func ParseTime(s string) (time.Time, error)
- func Version(space string) (string, bool)
- func WriteAtom(m FeedMeta, alerts []*Alert, now time.Time) ([]byte, error)
- type Alert
- type Area
- type Entry
- type FeedMeta
- type Info
- type NamedValue
- type Options
- type Problem
- type Reference
- type Resource
- type SignSettings
- type Signer
- type ValidationError
- type Verifier
- type VerifySettings
Examples ¶
Constants ¶
const ( Namespace11 = "urn:oasis:names:tc:emergency:cap:1.1" Namespace10 = "http://www.incident.com/cap/1.0" )
Namespaces of the earlier CAP versions, read like CAP 1.2 (Parse).
const ( SignatureValid = "valid" SignatureNone = "none" )
Signature states.
const AtomNamespace = "http://www.w3.org/2005/Atom"
AtomNamespace is the Atom (RFC 4287) namespace.
const MediaType = "application/cap+xml"
MediaType is the media type of a CAP document.
const Namespace = "urn:oasis:names:tc:emergency:cap:1.2"
Namespace is the CAP 1.2 XML namespace.
const XMLDSigNamespace = "http://www.w3.org/2000/09/xmldsig#"
XMLDSigNamespace is the XML Signature namespace.
Variables ¶
var ( Statuses = []string{"Actual", "Exercise", "System", "Test", "Draft"} MsgTypes = []string{"Alert", "Update", "Cancel", "Ack", "Error"} Scopes = []string{"Public", "Restricted", "Private"} Categories = []string{"Geo", "Met", "Safety", "Security", "Rescue", "Fire", "Health", "Env", "Transport", "Infra", "CBRNE", "Other"} Responses = []string{"Shelter", "Evacuate", "Prepare", "Execute", "Avoid", "Monitor", "Assess", "AllClear", "None"} Urgencies = []string{"Immediate", "Expected", "Future", "Past", "Unknown"} SeverityList = []string{"Extreme", "Severe", "Moderate", "Minor", "Unknown"} Certainties = []string{"Observed", "Likely", "Possible", "Unlikely", "Unknown"} )
Enumerations of the CAP 1.2 schema.
var ErrUnsigned = errors.New("the alert is not signed and signatures are required")
ErrUnsigned is an unsigned alert where signatures are required.
Functions ¶
func CirclePolygon ¶
CirclePolygon approximates a circle as a closed GeoJSON ring (spherical Earth, 6371 km).
func FormatTime ¶
FormatTime writes t in CAP's date-time form: seconds precision with an explicit offset ("2026-09-24T10:00:00+05:30"); UTC is written "-00:00".
func ParseCircle ¶
ParseCircle reads a CAP circle "lat,lon radius" (radius in km).
func ParsePolygon ¶
ParsePolygon reads a CAP polygon: at least four space-separated "lat,lon" pairs whose first and last are the same point. It returns the ring as GeoJSON positions.
func ParseTime ¶
ParseTime reads a CAP date-time. Strict CAP has seconds precision and a numeric offset; feeds in the field also use "Z" and fractions, which are accepted too.
Types ¶
type Alert ¶
type Alert struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:emergency:cap:1.2 alert"`
Identifier string `xml:"identifier"`
Sender string `xml:"sender"`
Sent string `xml:"sent"`
Status string `xml:"status"`
MsgType string `xml:"msgType"`
Source string `xml:"source,omitempty"`
Scope string `xml:"scope"`
Restriction string `xml:"restriction,omitempty"`
Addresses string `xml:"addresses,omitempty"`
Code []string `xml:"code,omitempty"`
Note string `xml:"note,omitempty"`
References string `xml:"references,omitempty"`
Incidents string `xml:"incidents,omitempty"`
Info []Info `xml:"info,omitempty"`
// Version is the CAP version the document was written in ("1.2",
// "1.1", "1.0", or "none" without a namespace); it is always
// marshalled as 1.2.
Version string `xml:"-"`
}
Alert is a CAP 1.2 alert message.
func Parse ¶
Parse decodes a CAP document: CAP 1.2, and also CAP 1.1, 1.0 or an alert without a namespace, read as 1.2 (Alert.Version says which). It checks the root element but not the content; call Validate for that.
func (*Alert) Expires ¶
Expires is the latest expiry of the alert's info blocks, or zero when one of them has none (it stays in effect).
func (*Alert) GeoJSON ¶
GeoJSON renders every area of the alert as a GeoJSON FeatureCollection: one Feature per area, with a Polygon, a MultiPolygon, or no geometry when the area is described by geocodes only. Circles become 32-sided polygons and keep their centre and radius in the properties.
func (*Alert) Key ¶
Key identifies a message: sender, identifier and sent together are unique (the same triple a references entry uses).
func (*Alert) MostSevere ¶
MostSevere is the highest severity among the info blocks ("Unknown" when none says).
type Area ¶
type Area struct {
AreaDesc string `xml:"areaDesc" json:"areaDesc"`
Polygon []string `xml:"polygon,omitempty" json:"polygon,omitempty"`
Circle []string `xml:"circle,omitempty" json:"circle,omitempty"`
Geocode []NamedValue `xml:"geocode,omitempty" json:"geocode,omitempty"`
Altitude string `xml:"altitude,omitempty" json:"altitude,omitempty"`
Ceiling string `xml:"ceiling,omitempty" json:"ceiling,omitempty"`
}
Area is an affected area. The JSON names are the CAP element names, so an area can be given in a JSON configuration.
type Entry ¶
type Entry struct {
ID string
Updated string
Title string
Link string // the CAP document's URL when it is not embedded
Alert *Alert // embedded alert, if any
// Raw is the embedded alert element's text (for signature checks); a
// bare CAP document's Raw is the whole document.
Raw []byte
}
Entry is one feed entry: an embedded CAP alert, or a link to one.
type FeedMeta ¶
type FeedMeta struct {
ID, Title, Author, Self string
// AlertURL gives the URL a CAP message is also served at.
AlertURL func(identifier string) string
}
FeedMeta describes a published Atom feed.
type Info ¶
type Info struct {
Language string `xml:"language,omitempty"`
Category []string `xml:"category"`
Event string `xml:"event"`
ResponseType []string `xml:"responseType,omitempty"`
Urgency string `xml:"urgency"`
Severity string `xml:"severity"`
Certainty string `xml:"certainty"`
Audience string `xml:"audience,omitempty"`
EventCode []NamedValue `xml:"eventCode,omitempty"`
Effective string `xml:"effective,omitempty"`
Onset string `xml:"onset,omitempty"`
Expires string `xml:"expires,omitempty"`
SenderName string `xml:"senderName,omitempty"`
Headline string `xml:"headline,omitempty"`
Description string `xml:"description,omitempty"`
Instruction string `xml:"instruction,omitempty"`
Web string `xml:"web,omitempty"`
Contact string `xml:"contact,omitempty"`
Parameter []NamedValue `xml:"parameter,omitempty"`
Resource []Resource `xml:"resource,omitempty"`
Area []Area `xml:"area,omitempty"`
}
Info is one info block (one language, one event).
type NamedValue ¶
type NamedValue struct {
ValueName string `xml:"valueName" json:"valueName"`
Value string `xml:"value" json:"value"`
}
NamedValue is an eventCode, parameter or geocode.
type Options ¶
type Options struct {
// LenientTime accepts "Z" and fractional seconds in date-times.
LenientTime bool
}
Options relax validation for feeds that stray from the schema.
type Problem ¶
Problem is one validation finding.
type Reference ¶
type Reference struct {
Sender, Identifier, Sent string
}
Reference is one entry of an alert's references.
func ParseReferences ¶
ParseReferences splits a references value into its entries.
type Resource ¶
type Resource struct {
ResourceDesc string `xml:"resourceDesc"`
MimeType string `xml:"mimeType"`
Size string `xml:"size,omitempty"`
URI string `xml:"uri,omitempty"`
DerefURI string `xml:"derefUri,omitempty"`
Digest string `xml:"digest,omitempty"`
}
Resource is a file attached to an info block.
type SignSettings ¶
type SignSettings struct {
// Key is the PEM private key (PKCS #8, PKCS #1 RSA or SEC 1 EC).
Key string `json:"key"`
// Certificate is the PEM certificate of the key, followed by any
// intermediates; the first goes in each signature's KeyInfo.
Certificate string `json:"certificate"`
}
SignSettings configures signing of published messages.
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer signs CAP documents.
func NewSigner ¶
func NewSigner(s SignSettings) (*Signer, error)
NewSigner reads a key and certificate chain.
func (*Signer) Certificates ¶
func (s *Signer) Certificates() []*x509.Certificate
Certificates is the signing certificate followed by its intermediates.
type ValidationError ¶
type ValidationError []Problem
ValidationError lists every problem of an alert.
func (ValidationError) Error ¶
func (v ValidationError) Error() string
func (ValidationError) Essential ¶
func (v ValidationError) Essential() bool
Essential tells whether any of the problems is essential.
type Verifier ¶
type Verifier struct {
// Now is the clock for certificate validity (tests); nil is time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Verifier checks CAP signatures against trusted certificates.
func NewVerifier ¶
func NewVerifier(s VerifySettings) (*Verifier, error)
NewVerifier reads the trusted certificates.
func NewVerifierSkipping ¶
func NewVerifierSkipping(s VerifySettings, skip func(i int, err error)) *Verifier
NewVerifierSkipping reads the trusted certificates, passing each entry that cannot be used to skip and leaving it out. With none left, every signed alert fails verification (it stays refused).
func (*Verifier) Trusted ¶
func (v *Verifier) Trusted() []*x509.Certificate
Trusted is the certificates whose signatures are accepted.
func (*Verifier) Verify ¶
Verify checks the document's signature. It returns the verified alert element as a document of its own (signature removed): callers parse that, never the original bytes, so content outside the signed element cannot be slipped in. An unsigned document is returned as it is with state "none" unless signatures are required.
type VerifySettings ¶
type VerifySettings struct {
// Trusted are PEM certificates whose signatures are accepted; a
// signature is valid only when its KeyInfo certificate is one of them
// (and within its validity period).
Trusted []string `json:"trusted"`
// Require refuses alerts without a valid signature. Without it an
// unsigned alert is accepted and marked "signature": "none"; a
// signature that does not verify is refused unless acceptInvalid.
Require bool `json:"require,omitempty"`
// AcceptInvalid stores an alert whose signature does not verify,
// marked "signature": "invalid", instead of refusing it (default
// false). Risk: a forged or tampered alert is stored and can raise
// alarms. Every such alert is also reported as a warning.
AcceptInvalid bool `json:"acceptInvalid,omitempty"`
}
VerifySettings configures checking of received signatures.