ADR-0025: Path Patterns as Key Namespaces with Regular Expression Matching
- Status: accepted
- Date: 2025-09-01
- Tags: Security, Policy, Architecture, UX
Context
SPIKE uses path patterns in two primary contexts:
- Secret paths: Logical namespaces where secrets are stored
(e.g.,
secrets/app/config
) - Policy patterns: Regular expressions used to match secret paths and SPIFFE IDs for access control
This creates potential confusion because:
- Secret paths are not Unix filesystem paths: They are logical key namespaces that happen to use slash-separated hierarchical notation for familiarity
- Policy patterns use regular expressions: Pattern matching in policies uses full regex syntax, not shell globs or simple wildcards
- Documentation and examples mix conventions: Some examples used to incorrectly suggest Unix path semantics or glob patterns: we have fixed those in the recent documentation updates.
The current implementation correctly uses regular expressions for policy matching, but the terminology and documentation can mislead users into thinking they’re working with filesystem paths or simple glob patterns.
Decision
We establish the following architectural principles for path handling in SPIKE:
1. Secret Paths Are Key Namespaces
Secret paths represent logical key namespaces, not filesystem paths:
- Paths SHOULD NOT start with a forward slash (
/
) - Paths are relative to the secrets engine mount point
- The slash separator (
/
) is used purely for hierarchical organization and familiarity - Future versions MAY restrict trailing slashes for consistency
Examples:
- ✅ Correct:
secrets/app/config
- ❌ Discouraged:
/secrets/app/config
2. Policy Patterns Use Regular Expressions
All pattern matching in SPIKE policies uses regular expressions:
spiffeidPattern
fields contain regex patterns, not globspathPattern
fields contain regex patterns, not globs- Patterns are compiled using Go’s
regexp.Compile()
- For exact matches, patterns should include
^
(start) and$
(end) anchors
Examples:
- ✅ Regex:
^secrets/app/.*$
(matches secrets/app/config, secrets/app/env, etc.) - ✅ Regex:
^spiffe://example\.org/service$
(exact match, escaped dots) - ❌ Not glob:
secrets/app/*
(this is NOT a valid SPIKE pattern) - ❌ Not glob:
spiffe://example.org/service*
(this is NOT a valid SPIKE pattern)
3. Terminology Clarification
- Path: Refers to secret storage locations (key namespaces)
- Pattern: Refers to regular expressions used in policies for matching
- Route or Namespace: Alternative terms that may be used to emphasize the non-filesystem nature of paths
Consequences
Positive
- Clear semantics: Users understand that paths are logical namespaces, not filesystem paths
- Powerful matching: Regular expressions provide precise and flexible pattern matching
- Consistent behavior: All pattern matching uses the same regex engine
- Predictable results: Users can test patterns using standard regex tools
- Security focused: Regex patterns allow for precise access control rules
Negative
- Learning curve: Users familiar with shell globs must learn regex syntax
- Complexity: Regular expressions can be more complex than simple wildcards
- Potential errors: Incorrect regex patterns can be overly permissive or overly restrictive
Neutral
- Documentation updates: All examples and documentation will use the correct terminology
Implementation Guidelines
For Secret Paths
- Validation: Paths must match
^[a-zA-Z0-9._\-/()?+*|[\]{}\\]+$
- Conventions: Should not start with
/
, should use/
for hierarchy - Examples:
secrets/database/creds
,config/app/production
For Policy Patterns
- Compilation: All patterns are compiled with
regexp.Compile(pattern)
- Anchoring: Use
^
and$
for exact matches - Escaping: Literal dots should be escaped as
\.
- Testing: Patterns can be tested using Go’s regex tools or online regex testers
Documentation Standards
- Always refer to policy “patterns” not “paths” when discussing regex matching
- Include regex examples with proper escaping and anchoring
- Clearly distinguish between secret paths (storage keys) and policy patterns (regex)
Examples
Secret Path Examples
secrets/app/config # Application configuration
secrets/database/production/creds # Production database credentials
config/service/tls # TLS configuration
tenantA/projectX/env1/key # Multi-tenant organization
Policy Pattern Examples
# Match all secrets under app namespace
pathPattern: "^secrets/app/.*$"
# Match specific database credentials
pathPattern: "^secrets/database/production$"
# Match numbered database instances
pathPattern: "^secrets/db-[0-9]+/creds$"
# Match SPIFFE ID with escaped dots
spiffeidPattern: "^spiffe://example\\.org/service$"
Migration Guide
For users transitioning from glob-style thinking:
Glob Pattern | SPIKE Regex Pattern | Notes |
---|---|---|
secrets/* | ^secrets/[^/]*$ | Single level only |
secrets/** | ^secrets/.*$ | All levels |
secrets/app* | ^secrets/app.*$ | Prefix match |
secrets/db-? | ^secrets/db-.$ | Single character |
secrets/db-[123] | ^secrets/db-[123]$ | Character class |