A Go package for working with Kopexa Resource Names (KRN), following Google's Resource Name Design.
go get github.com/kopexa-grc/krn//kopexa.com/{collection}/{resource-id}[/{collection}/{resource-id}][@{version}]
//{service}.kopexa.com/{collection}/{resource-id}[/{collection}/{resource-id}][@{version}]
| Component | Description | Example |
|---|---|---|
| Service | Optional service subdomain | catalog, isms, policy |
| Domain | Always kopexa.com |
kopexa.com |
| Collection | Resource type (plural) | frameworks, controls, tenants |
| Resource ID | Unique identifier | iso27001, 5.1.1, acme-corp |
| Version | Optional version tag | @v1, @v1.2.3, @latest, @draft |
//kopexa.com/frameworks/iso27001
//kopexa.com/frameworks/iso27001/controls/5.1.1
//catalog.kopexa.com/frameworks/iso27001
//catalog.kopexa.com/frameworks/iso27001/controls/5.1.1@v2
//isms.kopexa.com/tenants/acme-corp/workspaces/main
import "github.com/kopexa-grc/krn"
// Parse a KRN string
k, err := krn.Parse("//kopexa.com/frameworks/iso27001")
if err != nil {
log.Fatal(err)
}
// Use MustParse when you're confident the string is valid
k := krn.MustParse("//kopexa.com/frameworks/iso27001")
// Check if a string is a valid KRN
if krn.IsValid(s) {
// ...
}// Build a simple KRN
k, err := krn.New().
Resource("frameworks", "iso27001").
Build()
// Result: //kopexa.com/frameworks/iso27001
// Build a nested KRN with version
k, err := krn.New().
Resource("frameworks", "iso27001").
Resource("controls", "a-5-1").
Version("v2").
Build()
// Result: //kopexa.com/frameworks/iso27001/controls/a-5-1@v2
// Build a KRN with service
k, err := krn.New().
Service("catalog").
Resource("frameworks", "iso27001").
Build()
// Result: //catalog.kopexa.com/frameworks/iso27001parent := krn.MustParse("//kopexa.com/frameworks/iso27001")
// Create a child KRN
child, err := krn.NewChild(parent, "controls", "a-5-1")
// Result: //kopexa.com/frameworks/iso27001/controls/a-5-1
// Or from a string
child, err := krn.NewChildFromString(
"//kopexa.com/tenants/acme-corp",
"workspaces",
"main",
)k := krn.MustParse("//catalog.kopexa.com/frameworks/iso27001/controls/5.1.1@v1")
k.Service() // "catalog"
k.HasService() // true
k.FullDomain() // "catalog.kopexa.com"
k.Path() // "frameworks/iso27001/controls/5.1.1"
k.Version() // "v1"
k.HasVersion() // true
k.Basename() // "5.1.1"
k.BasenameCollection() // "controls"
k.Depth() // 2
// Get resource ID by collection
frameworkID, err := k.ResourceID("frameworks") // "iso27001"
controlID := k.MustResourceID("controls") // "5.1.1"
// Check if a collection exists
k.HasResource("frameworks") // true
k.HasResource("policies") // false
// Get all segments
for _, seg := range k.Segments() {
fmt.Printf("%s: %s\n", seg.Collection, seg.ResourceID)
}k := krn.MustParse("//kopexa.com/frameworks/iso27001/controls/a-5-1")
parent := k.Parent()
// Result: //kopexa.com/frameworks/iso27001
// Root resources return nil
root := krn.MustParse("//kopexa.com/frameworks/iso27001")
root.Parent() // nilk := krn.MustParse("//kopexa.com/frameworks/iso27001")
// Add version
versioned, err := k.WithVersion("v1.2.3")
// Result: //kopexa.com/frameworks/iso27001@v1.2.3
// Remove version
k2 := krn.MustParse("//kopexa.com/frameworks/iso27001@v1")
unversioned := k2.WithoutVersion()
// Result: //kopexa.com/frameworks/iso27001k := krn.MustParse("//kopexa.com/frameworks/iso27001")
// Add service
withService, err := k.WithService("catalog")
// Result: //catalog.kopexa.com/frameworks/iso27001
// Remove service
k2 := krn.MustParse("//catalog.kopexa.com/frameworks/iso27001")
withoutService := k2.WithoutService()
// Result: //kopexa.com/frameworks/iso27001k1 := krn.MustParse("//kopexa.com/frameworks/iso27001")
k2 := krn.MustParse("//kopexa.com/frameworks/iso27001")
k1.Equals(k2) // true
k1.EqualsString("//kopexa.com/frameworks/iso27001") // trueCompliance frameworks often have different editions (e.g., ISO 27001:2013 vs ISO 27001:2022). Include the edition year or version number in the framework resource ID:
// ISO 27001:2022
k := krn.MustParse("//catalog.kopexa.com/frameworks/iso27001-2022/controls/5.1.1")
// ISO 27001:2013 (different control numbering)
k := krn.MustParse("//catalog.kopexa.com/frameworks/iso27001-2013/controls/A.5.1.1")
// NIST CSF 2.0
k := krn.MustParse("//catalog.kopexa.com/frameworks/nist-csf-2.0/controls/GV.OC-01")
// CIS AWS Benchmark v1.4.0
k := krn.MustParse("//catalog.kopexa.com/frameworks/cis-aws-1.4.0/controls/1.1.1")
// SOC 2 Type II 2017
k := krn.MustParse("//catalog.kopexa.com/frameworks/soc2-2017/controls/CC1.1")Naming conventions:
- Use lowercase with hyphens:
iso27001-2022,nist-csf-2.0 - Include the year or version:
iso27001-2022,cis-aws-1.4.0 - Keep it readable:
pci-dss-4.0notpcidssv4.0
Note: The @version suffix (e.g., @v1, @draft) is for versioning the KRN content itself
(like draft vs published mappings), not for framework editions.
A common use case is mapping controls between frameworks:
// ISO 27001:2022 control
controlA := krn.MustParse("//catalog.kopexa.com/frameworks/iso27001-2022/controls/5.1.1")
// CIS AWS v1.5.0 control that maps to it
controlB := krn.MustParse("//catalog.kopexa.com/frameworks/cis-aws-1.5.0/controls/1.1.1")
// NIST CSF 2.0 control
controlC := krn.MustParse("//catalog.kopexa.com/frameworks/nist-csf-2.0/controls/GV.OC-01")
// In a mapping file:
// - krn: //catalog.kopexa.com/frameworks/iso27001-2022/controls/5.1.1
// maps_to:
// - krn: //catalog.kopexa.com/frameworks/cis-aws-1.5.0/controls/1.1.1
// - krn: //catalog.kopexa.com/frameworks/nist-csf-2.0/controls/GV.OC-01// Quick resource extraction from string
id, err := krn.GetResource("//kopexa.com/frameworks/iso27001", "frameworks")
// Result: "iso27001"
// Validate resource IDs
krn.IsValidResourceID("valid-id") // true
krn.IsValidResourceID("-invalid") // false
// Validate versions
krn.IsValidVersion("v1.2.3") // true
krn.IsValidVersion("latest") // true
krn.IsValidVersion("invalid") // false
// Validate service names
krn.IsValidService("catalog") // true
krn.IsValidService("Catalog") // false (must be lowercase)
krn.IsValidService("1svc") // false (can't start with number)
// Convert strings to valid resource IDs
krn.SafeResourceID("Hello World!") // "Hello-World"Service names must follow DNS label rules:
- Length: 1-63 characters
- Allowed characters:
a-z,0-9,- - Must start with a letter
- Cannot end with
-
Resource IDs must follow these rules:
- Length: 1-200 characters
- Allowed characters:
a-z,A-Z,0-9,-,_,. - Cannot start or end with
-or.
Supported version formats:
- Semantic:
v1,v1.2,v1.2.3 - Keywords:
latest,draft
The package exports typed errors for precise error handling:
import "errors"
k, err := krn.Parse(input)
if err != nil {
switch {
case errors.Is(err, krn.ErrEmptyKRN):
// Handle empty input
case errors.Is(err, krn.ErrInvalidKRN):
// Handle invalid format
case errors.Is(err, krn.ErrInvalidDomain):
// Handle wrong domain
case errors.Is(err, krn.ErrInvalidResourceID):
// Handle invalid resource ID
case errors.Is(err, krn.ErrInvalidVersion):
// Handle invalid version format
case errors.Is(err, krn.ErrResourceNotFound):
// Handle missing resource
}
}- @kopexa/krn - TypeScript implementation
- krn-fixtures - Shared test fixtures
Kopexa is a modern GRC (Governance, Risk, and Compliance) platform that helps organizations manage their compliance requirements efficiently.
- Website: kopexa.com
- Documentation: docs.kopexa.com
Copyright (c) Kopexa GRC
Licensed under the Apache License, Version 2.0. See LICENSE for details.