Database semantic models, declarations, QueryIR, and the canonical binary protocol for the database ecosystem.
The normative ownership and product contract is documented in database-kit Responsibility Specification.
The package implements the target-free DatabaseWire v2 contract by default.
The non-default MultipleBases trait adds Base, Composition, persisted Grant,
target, provenance, and federated-consistency semantics as DatabaseWire v3.
Completion is verified through Native behavior tests, standard and Embedded
WASM release builds, the Embedded macro-compilation fixture, and byte-owner
identity tests in both trait configurations.
database-kit is the semantic contract consumed by both database-framework and database-client. Primitive representations are owned by database-types; database-kit adds model, schema, query, graph, and protocol meaning.
It provides:
@Persistablemacro for defining data models@Polymorphablemacro for protocol-oriented polymorphic storage metadata@OWLClassmacro for OWL ontology class mapping@OWLDataProperty/@OWLObjectPropertymacros for OWL property annotationsIndexKindprotocol for extensible index type definitions- Foundation-independent identity, RDF, and
QueryIRsemantic models - bounded RDF term-role and structural validation without binary materialization
- canonical binary
DatabaseWireoperations, envelopes, limits, and errors - canonical schema manifests, SHA-256 fingerprints, and typed schema
plan/apply requests through
schema.execute - strict, lossless JSON adaptation for schema manifests in the optional
native
DatabaseSchemaJSONproduct - one Foundation-independent semantic module
- one canonical bounded binary protocol module
- one optional native Foundation model-integration module
With the non-default MultipleBases trait it additionally provides:
Base,Base.Composition, and persistedSecurity.Grantsemantics;- database, Base, or Composition operation targets;
- Base-qualified identity, placement, lifecycle, provenance, and federated read consistency;
- the
base.execute,composition.execute, andgrant.executeoperations.
database-client ────────┐
▼
database-kit ───────▶ database-types
▲
│
database-framework ─▶ storage-kit
dependencies: [
.package(
url: "https://github.com/1amageek/database-kit.git",
from: "26.0815.0"
)
]| Module | Description |
|---|---|
DatabaseKit |
Foundation-independent model, identity, schema, query, mutation, relationship, index, graph, ontology, SHACL, and shared streaming digest support |
DatabaseWire |
Canonical binary envelopes, typed operations, bounded encoding and decoding, results, errors, and protocol-specific digest values |
DatabaseSchemaJSON |
Native strict JSON adapter for SchemaManifest; rejects duplicate or unknown keys and preserves every FieldValue case without numeric inference |
DatabaseKitFoundation |
Native-only participation of Foundation scalar types in Persistable field adaptation |
Relationship, vector, full-text, geographic, rank, permutation, graph,
ontology, and SHACL are source classifications within DatabaseKit, not
separate products.
DatabaseKit and DatabaseWire build with the matching Swift 6.4 standard and
Embedded WASM SDKs. DatabaseSchemaJSON and DatabaseKitFoundation are native
adapter products and are excluded from that dependency graph.
There is no umbrella value module in database-kit. FieldValue and every
primitive alternative are defined only by DatabaseTypes; the database-kit
modules above add model, RDF, SHACL, query, and protocol semantics without
redeclaring that algebra.
Query pagination uses UInt64, so negative LIMIT and OFFSET values are not
representable. DatabaseWire encoding measures the exact frame and writes
directly into one final ByteString allocation; there is no public
mutable-array writer path.
The default graph has no Base declarations, target field, persisted Grant operations, provenance table, topology, or federated consistency payload. It represents one database and one execution root directly.
When MultipleBases is enabled, Base.ID, Base.Placement.ID, and
Base.Composition.ID are validated ASCII slugs. A Composition stores a
nonempty, unique, canonically ordered Base set. Requests then carry an explicit
database, Base, or Composition target. Composition result pages encode their
Base table once and attach ordinal provenance to each row or quad, together
with the transactional or federated read points that fixed the result.
.package(
url: "https://github.com/1amageek/database-kit.git",
from: "26.0815.0",
traits: [.trait(name: "MultipleBases")]
)See Architecture and ownership for the package boundary and dependency rules. See Zero-Copy and Embedded Architecture for the copy budget, static model-adaptation design, lazy result pages, and WASM host transport contract.
| Contract | Verification |
|---|---|
| Apple platform behavior | TOOLCHAINS=org.swift.64202607231a scripts/xcode-test-harness; the package-owned harness enforces the reviewed exact test count with zero skips, expected failures, runtime warnings, or internal tool errors |
| Standard WASM contract | Release builds of DatabaseKit and DatabaseWire with the matching Swift 6.4 WASM SDK and -debug-info-format none |
| Embedded semantic and Wire graph | Release builds of DatabaseKit and DatabaseWire with the matching Swift 6.4 Embedded WASM SDK and -debug-info-format none |
| Embedded macro use | Embedded release build of DatabaseKitDeclarationContract with -debug-info-format none, which expands model, field, directory, index, and relationship declarations |
| Binary ownership | Tests assert that payload pages borrow ranges from the single final frame allocation |
| Decoder safety | Tests cover truncation, limits, malformed values, non-canonical input, and cyclic RDF lists |
The current baseline is
swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a with the exactly matching
standard and Embedded WASM SDKs. The macro dependency requires swift-syntax
as a release range (from: "603.0.2") so downstream graphs that also
constrain swift-syntax (e.g. through JavaScriptKit) stay resolvable;
reproducibility comes from the committed Package.resolved, which CI enforces
with -onlyUsePackageVersionsFromResolvedFile. The full verification matrix
(host test harness plus the standard and Embedded WASM release builds below)
passes on this toolchain with swift-syntax 603.0.2.
Release/WASM verification disables debug information because it is not shipped
in the reactor and avoids running host-side dsymutil over macro dependency
objects. Compiler diagnostics remain enabled.
TOOLCHAINS=org.swift.64202607231a scripts/xcode-test-harness
swift build --disable-default-traits --product DatabaseWire
swift build --disable-default-traits --traits MultipleBases --product DatabaseWire
swift build --swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm --product DatabaseKit -c release -debug-info-format none
swift build --swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm --product DatabaseWire -c release -debug-info-format none
swift build --swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm-embedded --product DatabaseKit -c release -debug-info-format none
swift build --swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm-embedded --product DatabaseWire -c release -debug-info-format none
swift build --swift-sdk swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-07-23-a_wasm-embedded --target DatabaseKitDeclarationContract -c release -debug-info-format noneimport DatabaseKit
@Persistable
struct User {
#Directory<User>("app", "users")
#Index(.scalar, fields: [\User.email], unique: true)
#Index(.scalar, fields: [\User.createdAt])
var id: String
var email: String
var name: String
var createdAt: Timestamp
}let schema = try Schema(
entities: [
try User.schemaEntity,
try Product.schemaEntity,
],
version: Schema.Version(1, 0, 0)
)The @Persistable macro generates all required protocol conformances:
@Persistable
struct Product {
#Index(
.scalar,
fields: [\Product.category, \Product.price]
)
#Index(.scalar, fields: [\Product.name], unique: true)
var id: String
var name: String
var category: String
var price: Double
@Transient
var cachedDescription: String? // Excluded from persistence
}Generated code: persistableType, allFields, fieldSchemas, typed
fields, indexDescriptors, fieldAccessRules, concrete
PersistedFieldOutput traversal, canonical decoding, and Sendable
conformance. The model declares id and owns its generation policy.
The production encoder does not inspect a model through Mirror, Any, or a
runtime metatype. It writes each concrete property directly to the selected
output. PersistableFieldEncoder.encode(_:) is the explicit owned
materializer for callers that require [PersistableField]; Wire and storage
outputs can consume the generated traversal without that intermediate array.
When an execution path needs one field after typed access has been erased,
persistedFieldValue(for:) selects it by FieldIdentity through the same
generated traversal. It materializes only that field as FieldValue; the model
contract has no Any, any Sendable, dynamic-member, or reflection fallback.
PersistedEntityValue is the common read boundary for compiled values and
PersistedModel. Once a concrete model becomes an owned PersistedModel,
heterogeneous execution can read its canonical fields without reconstructing a
synthetic or concrete Persistable value. Its complete-field accessor preserves
the owned PersistedModel backing and materializes compiled values only at the
explicit type-erasure boundary.
Ontology bindings retain the complete field-to-property descriptor mapping in
Schema.Entity. A schema manifest therefore preserves the same RDF projection
contract as a compiled @OWLClass or @OWLObjectProperty model.
OWLCanonicalDataPropertyProjection and the canonical
OWLIndividualIRIBuilder overloads apply that contract directly to
FieldValue, so schema-driven runtimes do not reconstruct concrete Swift
models or pass through JSON before materializing RDF terms.
Polymorphic persistence lets multiple concrete @Persistable models share a
logical protocol group for storage and querying.
The intended API is:
@Polymorphable
@PolymorphicDirectory("memory", "entities")
@PolymorphicIndex(
.vector(dimensions: 256),
embedding: "embedding"
)
public protocol Entity: Polymorphable<EntityPolymorphicGroup> {
var label: String { get }
var embedding: Vector { get set }
}
@Persistable
public struct Person: Entity {
public var id: String
public var name: String
public var embedding: Vector
public var label: String { name }
}@Persistable owns Persistable conformance for concrete structs.
Polymorphable<Group> owns only static group membership. The domain protocol
requires the fields shared by its concrete models, while
EntityPolymorphicGroup is the generated metadata declaration.
@Polymorphable is a metadata and validation macro. Swift 6.4 does not allow an
attached macro on a protocol to add protocol inheritance, so the protocol must
explicitly bind the generated declaration with
: Polymorphable<EntityPolymorphicGroup>.
Schema versions may use different Swift protocol names while preserving one logical storage identity. Declare that stable identity explicitly:
@Polymorphable(identifier: "Document")
protocol DocumentV2: Polymorphable<DocumentV2PolymorphicGroup> {
var id: String { get }
}The identifier is part of persisted polymorphic membership and directory identity. It must remain unchanged across schema versions that represent the same logical group.
Swift 6.4 cannot form a KeyPath<Self, Value> while the protocol containing
the declaration is still being defined. Protocol-level indexes therefore use
logical property names with the @PolymorphicIndex attribute.
@Polymorphable validates every name against a declared protocol property at
compile time. Schema then resolves the logical property to each concrete
member's generated FieldIdentity and validates its canonical field type.
This string boundary is limited to protocol source declarations. Concrete
models continue to use KeyPath syntax with #Index, and runtime index
maintenance receives only concrete field identities. It performs neither
KeyPath retention nor string-to-field discovery.
See Polymorphic Persistence Design for the full design and migration plan.
#Directory<User>("app", "users")@Persistable
struct Order {
#Directory<Order>("tenants", \Order.accountID, "orders", layer: .partition)
var orderID: Int64
var accountID: String // Partition key
}@Persistable
struct Message {
#Directory<Message>(
"tenants", \Message.accountID,
"channels", \Message.channelID,
"messages",
layer: .partition
)
var messageID: String
var accountID: String
var channelID: String
var content: String
}Built-in indexes have one declaration surface: IndexDefinition through
#Index. The generic *IndexKind<Model> duplicates are not part of version 1.
@Persistable
struct Event {
#Index(
.scalar,
fields: [\Event.calendarID, \Event.startsAt]
)
#Index(.count, groupBy: [\Event.calendarID])
#Index(
.sum,
groupBy: [\Event.calendarID],
value: \Event.attendeeCount
)
#Index(
.version(strategy: .keepAll),
field: \Event.id
)
#Index(
.vector(dimensions: 384, metric: .cosine),
embedding: \Event.embedding
)
#Index(
.fullText(tokenizer: .simple),
fields: [\Event.title, \Event.description]
)
#Index(
.autocomplete(minPrefixLength: 2, maxPrefixLength: 12),
fields: [\Event.title, \Event.searchTerms]
)
#Index(.spatial(), location: \Event.location)
#Index(.rank, field: \Event.attendeeCount)
#Index(
.propertyGraph(strategy: .adjacency),
from: \Event.sourceID,
edge: \Event.relationship,
to: \Event.targetID
)
var id: String
var calendarID: String
var startsAt: Timestamp
var attendeeCount: Int64
var embedding: Vector
var title: String
var description: String
var searchTerms: [String]
var location: GeographicPoint
var sourceID: String
var relationship: String
var targetID: String
}The enclosing macro consumes each KeyPath and emits the corresponding
generated field identity. Protocol-level polymorphic declarations use
logical property names, which @Polymorphable verifies against protocol
properties.
Neither runtime path retains a KeyPath.
Property graphs and RDF datasets have separate declarations because their
identity contracts are different. A property graph uses String source,
label, target, and optional namespace fields. An RDF dataset uses RDFTerm
subject, predicate, object, and optional graph fields:
@Persistable
struct Statement {
#Index(
.rdfDataset,
from: \Statement.subject,
edge: \Statement.predicate,
to: \Statement.object,
graph: \Statement.graph
)
var id: String
var subject: RDFTerm
var predicate: RDFTerm
var object: RDFTerm
var graph: RDFTerm?
}The canonical kind identifiers remain graph and rdf_quad, respectively.
Schema validation never infers one graph model from the other.
IndexKind is reserved for OWL-generated and third-party extension semantics
that are not built into IndexDefinition. Property graph and RDF dataset
declarations are built in and use IndexDefinition:
import DatabaseKit
public struct TimeSeriesIndexKind<Root: Persistable>: IndexKind {
public static var identifier: String { "com.mycompany.timeseries" }
public static var subspaceStructure: SubspaceStructure { .hierarchical }
public let indexFields: [IndexField<Root>]
public let resolution: TimeResolution
public var indexName: String {
"\(Root.persistableType)_timeseries_\(fieldNames.joined(separator: "_"))"
}
public var metadata: [String: FieldValue] {
["resolution": .string(resolution.rawValue)]
}
public enum TimeResolution: String, Sendable, Hashable {
case second, minute, hour, day
}
public init(
fields: [IndexField<Root>],
resolution: TimeResolution = .minute
) {
self.indexFields = fields
self.resolution = resolution
}
public static func validateFields(
_ fields: [FieldSchema]
) throws(IndexValidationError) {
guard fields.count == 1 else {
throw .invalidFieldCount(
index: identifier,
expected: 1,
actual: fields.count
)
}
guard fields[0].type == .timestamp, !fields[0].isArray else {
throw .unsupportedField(
index: identifier,
field: fields[0],
reason: "Time-series fields must use timestamp values"
)
}
}
}Application use passes a generated field through the custom descriptor path:
let timeSeries = try IndexDescriptor(
name: "Event_timeseries_startsAt",
kind: TimeSeriesIndexKind<Event>(
fields: [Event.fields.startsAt.ascending],
resolution: .minute
)
)
let schema = try Schema(
entities: [
try Schema.Entity(
from: Event.self,
including: [timeSeries]
)
]
)Server-side maintenance is implemented and registered by
database-framework. Custom
declarations expose only canonical IndexKindMetadata; runtime behavior remains
outside this package. IndexDescriptor validates concrete generated fields and
configuration before Schema exposes the catalog.
@Persistable
enum Status: String {
case active, inactive, pending
}
@Persistable
struct Task {
var id: String
var title: String
var status: Status = .pending
}Applying @Persistable to a raw-value enum generates its PersistableEnum
conformance and case enumeration. String- and Int-backed enums receive
canonical field encoding and case metadata. Availability-qualified cases are
rejected so the persisted schema cannot vary by platform. @Persistable(type:)
remains specific to model structs.
@Persistable
struct Order {
var total: Double
@Relationship(Customer.self, indexFields: [\.name])
var customerID: String?
}Ontology features are in the Graph module. Three usage levels can be combined incrementally.
| Level | Components | Use Case |
|---|---|---|
| 1. OntologyStore | OWLOntology, context.ontology API |
OWL reasoning, class hierarchy, property chain evaluation |
| 2. Macros + OntologyStore | Level 1 + @OWLClass, @OWLObjectProperty, @OWLDataProperty |
Bind Persistable types to OWL concepts, IRI validation, SPARQL over tables |
| 3. Macros + OntologyStore + Triples | Level 2 + graph and RDF index declarations | SPARQL federation across Persistable tables and RDF triples |
Define and load OWL ontologies for reasoning and hierarchy queries. No macros required.
var ontology = OWLOntology(iri: "http://example.org/onto")
ontology.classes = [OWLClass(iri: "ex:Person"), OWLClass(iri: "ex:Employee")]
ontology.axioms = [.subClassOf(sub: .named("ex:Employee"), sup: .named("ex:Person"))]
// Load and query (server-side, database-framework)
try await context.ontology.load(ontology)
let reasoner = try await context.ontology.reasoner(for: "http://example.org/onto")
let superClasses = reasoner.superClasses(of: "ex:Employee")Bind Persistable types to OntologyStore concepts. Macros are bindings, not definitions — class hierarchies, property characteristics, and axioms live in the OntologyStore. Each row is interpreted as virtual RDF triples, enabling SPARQL queries over Persistable tables.
@Persistable
@OWLClass("ex:Employee")
struct Employee {
@OWLDataProperty("ex:name")
var name: String
@OWLDataProperty("ex:worksFor", to: \Department.id, functional: true)
var departmentID: String?
}
@Persistable
@OWLObjectProperty("ex:employs", from: "employeeID", to: "projectID")
struct Assignment {
var id: String = UUID().uuidString
var employeeID: String = ""
var projectID: String = ""
@OWLDataProperty("ex:since")
var startDate: Timestamp
}IRI validation checks that macro bindings reference valid OntologyStore entries:
try await context.ontology.validateSchema(schema, ontologyIRI: "http://example.org/onto")SPARQL queries Persistable tables directly:
let results = try await context.sparql()
.from(Employee.self)
.where("?e", "rdf:type", "ex:Employee")
.where("?e", "ex:name", "?name")
.select("?e", "?name")
.execute()Add a GraphIndex triple store alongside Persistable tables. SPARQL federation resolves each triple pattern to the optimal source — structured data in tables, unstructured knowledge in triples:
let results = try await context.sparql()
.from(RDFTriple.self) // Triple store
.from(Employee.self) // Persistable table
.where("?e", "rdf:type", "ex:Employee") // → Employee table
.where("?e", "ex:worksFor", "?dept") // → Employee table
.where("?dept", "ex:locatedIn", "?city") // → Triple store
.select("?e", "?city")
.execute()@OWLClass(_ iri: String) — Maps a Persistable type to an OWL class. Generates OWLClassEntity conformance, ontologyClassIRI, and ontologyPropertyDescriptors.
@OWLObjectProperty(_ iri: String, from: String, to: String) — Maps a Persistable type to an OWL ObjectProperty with endpoint fields. Generates OWLObjectPropertyEntity conformance and an adjacency graph index declaration with an implicit label.
@OWLDataProperty(_ iri: String, ...) — Annotates a field with an OWL datatype property IRI.
Bare names (without :, #, or /) default to the namespace extracted from the parent @OWLClass or @OWLObjectProperty IRI.
| Platform | Minimum Version |
|---|---|
| iOS | 26.0+ |
| macOS | 26.0+ |
| tvOS | 26.0+ |
| watchOS | 26.0+ |
| visionOS | 26.0+ |
| Linux | Swift 6.4+ |
| WASI Embedded | Swift 6.4+ for DatabaseKit and DatabaseWire |
| WASI (standard runtime) | Swift 6.4+ for the same semantic and wire products |
| Package | Role | Platform |
|---|---|---|
| database-types | Primitive field-value algebra and immutable byte ownership | Embedded, Apple, Linux |
| database-framework | Database execution, transactions, indexes, graph, ontology, and validation | WASI, macOS, Linux |
| database-client | Typed invocation and WASM host, HTTP, and WebSocket transports | Embedded, Apple, Linux |
| storage-kit | Storage transactions and backend adapters | WASI, macOS, Linux |
Licensed under the MIT License.