The Design of Misting URIs: A Journey to Clarity
Introduction
Every infrastructure management system needs a way to uniquely identify resources. In Mistcraft, we designed the Misting URI schema—a standardized way to reference any resource type or instance across all providers. This article tells the story of how we arrived at our current design, the alternatives we considered, and the lessons we learned along the way.
The Challenge: Create a URI schema that is readable, hierarchical, URL-safe, and flexible enough to handle resources from any cloud provider while maintaining consistency.
The Requirements
Before diving into design decisions, we established clear requirements for our URI system:
Unique Identification
Every resource type and instance must have a globally unique identifier
Human Readable
URIs should be understandable at a glance, not cryptic UUIDs
Hierarchical
Support nested resources and clear organizational structure
URL Compatible
Work within standard URL contexts with minimal encoding
Version Aware
Support provider versioning for API evolution
Extensible
Accommodate future providers and resource types
The Final Structure
After extensive iteration, we settled on this URI structure:
# General format
mw:<classification>:<provider>[@<version>]/<path>
# Type URIs - identify resource types
mw:type:azure/compute/virtual-machine
mw:type:azure@v1.0.0/storage/storage-account
mw:type:aws/ec2/instance
# Resource URIs - identify specific instances
mw:resource:azure/subscriptions/d4b54c68/resourceGroups/myRg
mw:resource:aws/arn:aws:ec2:us-west-2:123456789012:instance/i-1234567890abcdef0Let's break down each component and the reasoning behind it:
mw: - The Scheme
We chose mw (Mistwrite/Misting) as our URI scheme identifier. This follows
RFC 3986 and immediately identifies these as Mistcraft URIs, preventing any confusion with
other systems.
:type: or :resource: - Classification
The second component classifies what kind of URI this is. This clear semantic boundary tells you immediately whether you're looking at a resource type definition or a specific instance reference.
provider - Provider Identifier
Simple, lowercase provider names like azure, aws, or gcp.
@v1.0.0 - Optional Version
Provider API versioning using the familiar @ notation from NPM and Docker.
The v prefix makes it unmistakably a version number.
/service/resource - The Path
Standard path notation for the resource hierarchy. Services like compute or
storage prevent naming collisions, while the path naturally supports nested
resources.
Design Rationale
Why Colons as Delimiters?
The use of colons creates clear semantic boundaries. When you see mw:type:azure,
each colon marks a significant transition: scheme → classification → provider. This is more
readable than alternatives like mw/type/azure where everything blurs together.
Why Version at the Provider Level?
Cloud providers version their entire API, not individual resources. When Azure releases API version 2021-01-01, it affects all resources. Our versioning scheme reflects this reality:
# Provider-level versioning (what we chose)
mw:type:azure@v2.0/compute/virtual-machine
mw:type:azure@v2.0/storage/storage-account
# NOT resource-level versioning
mw:type:azure/compute/virtual-machine@v2.0 # Wrong level!Why the @ Symbol for Versions?
We considered several version notation options:
| Option | Example | Decision |
|---|---|---|
| @ notation | azure@v1.0.0 | ✓ Chosen - Familiar from NPM/Docker |
| Query parameter | azure?v=1.0.0 | Too optional-looking for critical info |
| Path segment | azure/v1.0.0/ | Makes paths longer and harder to parse |
| Colon separator | azure:v1.0.0 | Too many colons, reduces clarity |
The Service Layer
Including a service namespace (like /compute/ or /storage/) prevents
naming collisions. Many providers have resources with the same name in different services:
# Without service layer - ambiguous!
mw:type:azure/instance # Is this compute? Container? Database?
# With service layer - clear
mw:type:azure/compute/instance
mw:type:azure/container/instance
mw:type:azure/database/instanceAlternatives We Considered
Alternative 1: Single Delimiter
mw/type/azure/compute/virtual-machineWhy we rejected it: Less semantic clarity. The boundary between classification and provider isn't visually distinct. Everything runs together.
Alternative 2: All Colons / URN-Style
mw:type:azure:1.0.0:compute:virtual-machineWhy we rejected it: "I didn't like it much" - actual quote from our design doc. More seriously, it's hard to parse visually and doesn't follow any established conventions. URNs are meant for persistent, location-independent names. Our URIs often reference actual cloud resources with locations.
Alternative 3: Fully Hierarchical Paths
mw://azure.compute.virtual-machine/v1.0.0Why we rejected it: The double-slash implies network location (like HTTP), which these aren't. Also, dots don't convey the same hierarchical feeling as slashes.
Real-World Examples
Here's how our URI schema handles various real-world scenarios:
Nested Resources
# Storage hierarchy in Azure
mw:type:azure/storage/storage-account
mw:type:azure/storage/storage-account/blob-container
mw:type:azure/storage/storage-account/blob-container/blob
# Kubernetes resources
mw:type:k8s/apps/deployment
mw:type:k8s/apps/deployment/replica-set
mw:type:k8s/apps/deployment/replica-set/podVersion Evolution
# Using latest (no version specified)
resource vm1 = mw:type:azure/compute/virtual-machine { ... }
# Pinning to specific version for stability
resource vm2 = mw:type:azure@v1.0/compute/virtual-machine { ... }
# Testing new beta features
resource vm3 = mw:type:azure@v2.0-beta/compute/virtual-machine { ... }Cross-Provider Consistency
# Similar resources across providers - consistent pattern
mw:type:azure/compute/virtual-machine
mw:type:aws/ec2/instance
mw:type:gcp/compute/instance
# Storage across providers
mw:type:azure/storage/storage-account
mw:type:aws/s3/bucket
mw:type:gcp/storage/bucketLessons Learned
1. Familiarity Matters
Using conventions developers already know (like @ for versions) reduces cognitive
load. Don't reinvent the wheel unless you have a compelling reason.
2. Visual Hierarchy is Important
The mixed use of colons and slashes creates natural groupings that help the eye parse URIs
quickly. mw:type:azure/compute/vm has clear sections, while
mw/type/azure/compute/vm is just a blur of slashes.
3. Plan for URL Encoding
While our URIs are elegant on their own, we had to accept that colons become %3A
in URLs. It's unfortunate but unavoidable—the clarity in non-URL contexts was worth the trade-off.
4. Validation Patterns Are Critical
We developed comprehensive regex patterns for validation early. This ensured consistency across all implementations and caught many edge cases during development:
^mw:type:(?P<provider>[a-z0-9-]+)
(?:@(?P<version>v\d+(?:\.\d+)?(?:\.\d+)?))?
/(?P<service>[a-z0-9-]+)
/(?P<resource_path>[a-z0-9-]+(?:/[a-z0-9-]+)*)$5. Leave Room for Evolution
The classification component (:type: vs :resource:) gives us room
to add new URI types in the future without breaking existing ones. Always design for extensibility.
Conclusion
The Misting URI schema represents dozens of iterations and heated debates. What started as a simple identifier system evolved into a carefully crafted balance between clarity, standards compliance, and practical usability.
Is it perfect? No. The URL encoding issue still bothers us. But it achieves our primary goals: developers can understand URIs at a glance, the system handles complex hierarchies elegantly, and it's flexible enough to grow with Mistcraft's needs.
The Most Important Lesson
Sometimes the best design isn't the most technically elegant—it's the one that makes developers'
lives easier. Our URI schema may require URL encoding, and yes, we use multiple delimiter types,
but when a developer sees mw:type:azure@v1.0/compute/virtual-machine, they know
exactly what it means. That clarity is worth more than technical purity.
Try It Yourself
See the Misting URI schema in action with Mistcraft.