Server-side database engine built on FoundationDB, implementing index maintenance for database-kit models.
database-framework is the server execution layer that provides:
- FDBContainer / FDBContext: Change-tracking data access with ACID transactions
- 12 index maintainers: Scalar, Vector, FullText, Spatial, Graph, Aggregation, and more
- Schema Registry: pg_catalog-style metadata persistence
- Migration System: Online index building and schema evolution
- DatabaseServer: WebSocket server for database-client connections
- DatabaseCLI: Interactive REPL for data inspection
┌──────────────────────────────────────────────────────────┐
│ database-kit │
│ @Persistable models, IndexKind protocols, QueryIR │
└──────────┬───────────────────────────────┬───────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────┐
│ database-framework │ │ database-client │
│ FDBContainer │◄─────│ DatabaseContext │
│ Index Maintainers │ WS │ KeyPath queries │
│ FoundationDB │ │ iOS / macOS │
└─────────────────────┘ └─────────────────────────┘
- FoundationDB 7.3+ installed and running
- Swift 6.2+
- macOS 15+ or Linux
# macOS
brew install foundationdb
brew services start foundationdbdependencies: [
.package(url: "https://github.com/1amageek/database-framework.git", from: "26.0223.0")
]import Database
import Core
// 1. Define models (from database-kit)
@Persistable
struct User {
#Directory<User>("app", "users")
#Index(ScalarIndexKind<User>(fields: [\.email]), unique: true)
var email: String
var name: String
}
// 2. Create container
let schema = Schema([User.self])
let container = try await FDBContainer(for: schema)
// 3. Insert data
let context = container.newContext()
context.insert(User(email: "alice@example.com", name: "Alice"))
context.insert(User(email: "bob@example.com", name: "Bob"))
try await context.save()
// 4. Query
let users = try await context.fetch(User.self)
.where(\.name == "Alice")
.execute()| Module | Description |
|---|---|
DatabaseEngine |
FDBContainer, FDBContext, IndexMaintainer protocol, Schema Registry |
Database |
All-in-one re-export of all index modules |
QueryAST |
SQL/SPARQL parser, AST builder, serializer |
DatabaseServer |
WebSocket server for database-client connections |
DatabaseCLICore |
Interactive REPL library for data inspection |
| Module | IndexKind | Features |
|---|---|---|
ScalarIndex |
ScalarIndexKind |
Range queries, sorting, unique constraints |
VectorIndex |
VectorIndexKind |
HNSW and flat similarity search |
FullTextIndex |
FullTextIndexKind |
Tokenization, inverted index |
SpatialIndex |
SpatialIndexKind |
S2 / Geohash / Morton encoding |
RankIndex |
RankIndexKind |
Skip-list based rankings |
GraphIndex |
GraphIndexKind |
Graph traversal, SPARQL, OWL DL reasoning |
AggregationIndex |
Count/Sum/Min/Max/AverageIndexKind |
Atomic aggregations |
VersionIndex |
VersionIndexKind |
FDB versionstamp history |
BitmapIndex |
BitmapIndexKind |
Roaring bitmap for categorical data |
LeaderboardIndex |
TimeWindowLeaderboardIndexKind |
Time-windowed leaderboards |
PermutedIndex |
PermutedIndexKind |
Alternative field orderings |
RelationshipIndex |
RelationshipIndexKind |
Cross-type relationship queries |
| Component | Role | Transactions |
|---|---|---|
FDBContainer |
Resource manager (DB connection, Schema, Directory) | Does NOT create |
FDBContext |
User-facing API, change tracking, batch operations | Creates |
FDBDataStore |
Low-level operations within transactions | Receives |
FDBContainer (owns FDBDatabase)
└── newContext() → FDBContext (references container, does NOT own database)
Startup: FDBClient.initialize() → FDBContainer.init → container.newContext()
Teardown: context = nil → container = nil → FDBClient.shutdown() [optional]
| Component | Owns Database? | Cleanup |
|---|---|---|
FDBContainer |
Yes (strong ref) | ARC — releases database on dealloc |
FDBContext |
No (references container) | ARC — discards pending changes |
Process exit: Safe without explicit cleanup. OS reclaims all resources.
Explicit shutdown (servers, tests):
// 1. Release contexts (discard pending changes)
context = nil
// 2. Release container (triggers fdb_database_destroy via ARC)
container = nil
// 3. Stop network (optional — OS handles this at process exit)
FDBClient.shutdown()Server pattern: One container per process, one context per request.
CLI pattern: One container, one context, Foundation.exit(0) is safe.
Test pattern: One container per suite, FDBClient.shutdown() in teardown.
let context = container.newContext()
// Stage changes
context.insert(user1)
context.insert(user2)
context.delete(user3)
// Commit all in one ACID transaction
try await context.save()database-kit defines what to index. database-framework defines how to maintain it.
// database-kit: metadata only
public struct ScalarIndexKind: IndexKind {
public static let identifier = "scalar"
}
// database-framework: FDB-dependent implementation
extension ScalarIndexKind: IndexKindMaintainable {
public func makeIndexMaintainer<Item: Persistable>(...) -> any IndexMaintainer<Item> {
return ScalarIndexMaintainer(...)
}
}// Declarative access control
extension Post: SecurityPolicy {
static func allowGet(resource: Post, auth: (any AuthContext)?) -> Bool {
resource.isPublic || resource.authorID == auth?.userID
}
}
// Encryption at rest (AES-256-GCM)
let config = TransformConfiguration(
encryptionEnabled: true,
keyProvider: RotatingKeyProvider(...)
)
let container = try FDBContainer(for: schema, transformConfiguration: config)let sql = """
SELECT * FROM User
WHERE id IN (SPARQL(Connection, 'SELECT ?from WHERE { ?from "follows" "bob" }'))
AND age > 18
"""
let users = try await context.executeSQL(sql, as: User.self)let migration = Migration(
fromVersion: Schema.Version(1, 0, 0),
toVersion: Schema.Version(2, 0, 0),
description: "Add email index"
) { ctx in
try await ctx.addIndex(emailIndexDescriptor)
}Link Persistable types to OWL ontology classes with @OWLClass and @OWLDataProperty / @OWLObjectProperty macros (defined in database-kit Graph module). @Persistable handles pure persistence; @OWLClass handles OWL class mapping independently.
import Database
import Core
import Graph
// 1. Define ontology-aware types
@Persistable
@OWLClass("http://example.org/onto#Employee")
struct Employee {
#Directory<Employee>("app", "employees")
@OWLDataProperty("http://example.org/onto#name")
var name: String
@OWLDataProperty("http://example.org/onto#worksFor", to: \Department.id)
var departmentID: String?
}
@Persistable
@OWLClass("http://example.org/onto#Department")
struct Department {
#Directory<Department>("app", "departments")
var name: String
}
// 2. Define object property (edge type) with @OWLObjectProperty
@Persistable
@OWLObjectProperty("http://example.org/onto#worksFor", from: "employeeID", to: "departmentID")
struct WorksFor {
#Directory<WorksFor>("app", "assignments")
var employeeID: String = ""
var departmentID: String = ""
@OWLDataProperty("http://example.org/onto#since")
var startDate: Date = Date()
}
// 3. Define RDF triple store
@Persistable
struct RDFTriple {
#Directory<RDFTriple>("app", "knowledge")
var subject: String = ""
var predicate: String = ""
var object: String = ""
#Index(GraphIndexKind<RDFTriple>(
from: \.subject, edge: \.predicate, to: \.object,
strategy: .hexastore
))
}
// 4. Create container
let schema = Schema([Employee.self, Department.self, WorksFor.self, RDFTriple.self])
let container = try await FDBContainer(for: schema)
// 5. Load ontology independently via OntologyStore
let ontology = OWLOntology(iri: "http://example.org/onto") {
Class("ex:Employee", subClassOf: "ex:Person")
Class("ex:Department")
ObjectProperty("ex:worksFor", domain: "ex:Employee", range: "ex:Department")
}
let context = container.newContext()
try await context.ontology.load(ontology)What happens automatically:
@OWLClassgeneratesOWLClassEntityconformance withontologyClassIRI@OWLClassscans@OWLDataPropertyannotations to buildontologyPropertyDescriptors@OWLObjectPropertygeneratesOWLObjectPropertyEntityconformance withobjectPropertyIRI,fromFieldName,toFieldName, and auto-generates aGraphIndexKindfor the edge type@OWLDataProperty(to:)generates a reverse index ondepartmentIDfor efficient lookupscontext.ontology.load()persists the ontology to/_ontology/via OntologyStore- GraphIndex uses the ontology for OWL 2 RL materialization on triple writes
See GraphIndex README for SPARQL, OWL reasoning, and graph algorithms.
@Polymorphable
protocol Document: Polymorphable {
var id: String { get }
var title: String { get }
}
// Query all conforming types from a shared directory
let docs = try await context.fetchPolymorphic(Article.self)[directory]/R/[type]/[id] → Protobuf-encoded item
[directory]/I/[indexName]/[values]/[id] → Index entry
[directory]/state/[indexName] → Index state (readable/writeOnly/disabled)
/_catalog/[typeName] → JSON-encoded TypeCatalog
swift build # Debug build
swift build -c release # Release build
swift test # All tests (requires FoundationDB)
swift test --filter DatabaseEngineTests # Engine tests only| Platform | Support |
|---|---|
| macOS | 15.0+ |
| Linux | Swift 6.2+ |
iOS/watchOS/tvOS/visionOS are not supported (requires FoundationDB).
| Package | Role | Platform |
|---|---|---|
| database-kit | Model definitions, IndexKind protocols, QueryIR | iOS, macOS, Linux |
| database-client | Client SDK with KeyPath queries and WebSocket transport | iOS, macOS |
MIT License