AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Kotlin RDF stack: RDF 1.2, SHACL 1.2, graph & SPARQL DSLs, Kastor Gen (KSP), ontology-quality tooling; backends: Jena, RDF4J, memory, SPARQL.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
More marks: logo variants (dark background, icon, wordmark).
Documentation website — Jekyll site in docs/, deployed with GitHub Actions. On a fork, enable Settings → Pages → Build and deployment: GitHub Actions.
Reading the docs: How documentation fits together · Glossary.
Contributors: repository architecture (Gradle modules, layers, and on-disk layout) and CONTRIBUTING (build, tests, CI).檢視完整 README ↗Documentation website — Jekyll site in docs/, deployed with GitHub Actions. On a fork, enable Settings → Pages → Build and deployment: GitHub Actions.
Reading the docs: How documentation fits together · Glossary.
Contributors: repository architecture (Gradle modules, layers, and on-disk layout) and CONTRIBUTING (build, tests, CI).
Kastor is a modern, comprehensive Kotlin framework for RDF (Resource Description Framework) and semantic web development. It bridges the gap between traditional object-oriented programming and semantic technologies, making RDF accessible and powerful for Kotlin developers.
💝 Support Kastor: If this project helps you, consider sponsoring to ensure continued maintenance and feature development. Organizations using Kastor in production can contact us for enterprise support and custom adaptations.
See release contracts and verification for tested capabilities, resource ownership, generated snapshots, and experimental limitations.
Use Kastor when you want domain-first RDF in Kotlin: pure domain interfaces with a side-channel back to RDF, plus a vocabulary-agnostic DSL that keeps RDF explicit without forcing RDF types into your business code.
Philosophy
Anti-goals
Comparison (neutral)
| Concern | Kastor | Jena / RDF4J / rdflib |
|---|---|---|
| Domain objects without RDF dependencies | ✅ | ❌ |
| Side-channel RDF access | ✅ | ❌ (typically direct RDF types) |
| Vocabulary-agnostic DSL | ✅ | ⚠️ (varies) |
| Provider-agnostic core API | ✅ | ❌ (engine-specific APIs) |
Kastor doesn't replace Jena or RDF4J—it makes them easier to use in Kotlin.
✅ Keep your existing infrastructure: Use your current repositories, stores, and configurations
✅ No migration required: Your data and queries work as-is
✅ Gradual adoption: Use Kastor for new code, keep existing code unchanged
✅ Full access: Reach underlying Jena/RDF4J APIs when needed
✅ Same backends: TDB2, NativeStore, memory stores—all supported
RDF-H 0.8: the optional rdf-holon module resolves an explicit holon registry over named graphs,
RDF 1.2 reifiers, or declared mixed data. It supports direct, aggregated and visible content,
attribution, checked conversion, and scoped SHACL validation. See RDF-H usage.
🎨 Natural language DSL: person has FOAF.name with "Alice" instead of verbose Model API calls
🔒 Type-safe SPARQL: Compile-time validated queries with Kotlin-idiomatic builders
🚀 Kotlin idioms: Extension functions, sealed classes, null safety, coroutines
🔄 Provider-agnostic: Switch between Jena, RDF4J, Memory, or SPARQL endpoints without changing your code
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.jena.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import org.apache.jena.rdf.model.Model
import org.apache.jena.rdf.model.ModelFactory
// Your existing Jena Model
val jenaModel: Model = ModelFactory.createDefaultModel()
// Wrap it with Kastor for easier Kotlin development.
// Reads are strict: a statement Kastor cannot represent (e.g. xml:lang="en_US") fails the read.
// Use JenaBridge.fromJenaModel(jenaModel, strictRead = false) to skip such statements instead.
val graph = jenaModel.toKastorGraph()
// Now use Kastor's DSL
graph.add {
val person = iri("http://example.org/alice")
person has FOAF.name with "Alice"
person has FOAF.age with 30
}
// Or access Jena directly when needed
val underlyingModel = graph.toJenaModel()
val statement = underlyingModel.listStatements().next()
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import org.eclipse.rdf4j.repository.Repository
// Your existing RDF4J repository
val rdf4jRepo: Repository = // ... your existing setup
// Wrap the initialized repository. Closing the adapter also shuts down rdf4jRepo.
// Graph reads are strict; pass Rdf4jRepository(rdf4jRepo, inference = false, lenientRead = true)
// to skip statements Kastor cannot represent instead of failing.
val repo = com.geoknoesis.kastor.rdf.rdf4j.Rdf4jRepository(rdf4jRepo)
// Now write cleaner Kotlin code
repo.add {
val person = iri("http://example.org/alice")
person has FOAF.name with "Alice"
}
// Same API works with Jena, Memory, or SPARQL endpoints
val jenaRepo = Rdf.repository {
providerId = "jena"
variantId = "tdb2"
location = "/data/tdb2"
}
// Same code, different backend!
Bottom line: Kastor is a compatibility layer that makes RDF programming easier in Kotlin. You keep your existing infrastructure and get a better developer experience.
Use the kastor-bom when you want every published module pinned to one version. When trimming the classpath:
implementation("com.geoknoesis.kastor:rdf-core") plus rdf-jena or rdf-rdf4j (Maven artifact IDs).RdfReasoning, ReasonerRegistry): add rdf-reasoning and the adapter-specific rdf-jena-reasoning and/or rdf-rdf4j-reasoning artifacts (they are no longer pulled in transitively by the store adapters).See Repository architecture — Dependency profiles for the full table.
Kastor provides a dual-layer architecture that serves both RDF experts and application developers:
rdf-testkit, rdf-cli; see Testing RDF graphs)rdf:reifies and triple terms (Jena and RDF4J providers)person `is` FOAF.Person)The name Kastor is a tribute to Castor, the pioneering Java XML data binding framework developed by Exolab. Castor was one of the first frameworks to bridge the gap between object-oriented programming and structured data formats, making it easier for developers to work with XML data in Java applications.
Just as Castor revolutionized XML data binding in Java, Kastor aims to bring the same level of developer experience to RDF and semantic web technologies in Kotlin. The "K" in Kastor represents:
Kastor honors Castor's legacy while embracing the future of semantic technologies and the Kotlin ecosystem.
Core Standards
rdf:reifies, rdf:dirLangString)<<( s p o )>> syntax, LANGDIR / STRLANGDIR, TRIPLE family)Validation & Semantics
rdf-jena or rdf-rdf4j (see SHACL validation)Serialization Formats
Other
rdf:reifies (RDF 1.2 native; provider-dependent)The bundled in-memory provider is RDF 1.1 only. RDF 1.2 features (triple terms, directional language strings, the new format syntaxes) require
rdf-jenaorrdf-rdf4jon the classpath. See docs/kastor/concepts/rdf-1.2.md and the migration guide for upgrading from 0.1.x.
Conformance: Kastor includes a W3C RDF 1.2 syntax harness in :rdf:conformance (Turtle 1.2, TriG 1.2, N-Triples 1.2, N-Quads 1.2) for Jena and RDF4J. Use ./gradlew conformanceSmokeTest for a fast bundled check; run python scripts/fetch-conformance-data.py (pinned W3C corpora) and then ./gradlew :rdf:conformance:test for the full upstream corpus. Details:
docs/kastor/concepts/rdf-1.2-conformance.md.
rdf-testkit — RDF-isomorphic assertions and golden Turtle checks; rdf-cli — parse, to-turtle, and diff from the command line (guide)person `is` FOAF.Person, person has FOAF.name with "Alice")reifies(...) DSL for statement metadata (replaces the RDF-star model of 0.1.x)qname("foaf:name") resolves declared and built-in prefixes to IRIsKey Benefits:
Rdf.repository { }withSelectRows, withConstructTriples and Rdf.openTripleStream release backend resources deterministically (release contract)Required runtime dependency:
Rdf.memory()andRdf.persistent()need a SPARQL-capable provider on the classpath. Add eithercom.geoknoesis.kastor:rdf-jenaorcom.geoknoesis.kastor:rdf-rdf4jto your dependencies. Without one, both factories throwRdfProviderException. The bundled:rdf:core-only memory provider is graph-only and must be opted into explicitly viaRdf.repository { providerId = "memory" }.
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.RDF
// Create a repository (needs rdf-jena or rdf-rdf4j on the classpath)
val repo = Rdf.memory()
repo.add {
prefixes {
put("ex", "http://example.org/")
}
val alice = qname("ex:alice") // QName → <http://example.org/alice>
val bob = iri("http://example.org/bob")
alice - RDF.type - FOAF.Person // minus-operator form
bob `is` FOAF.Person // natural-language alias for rdf:type
alice - FOAF.name - "Alice" // strings become xsd:string literals
alice has FOAF.age with 30 // has/with form; Int becomes xsd:integer
alice[FOAF.knows] = bob // bracket form
}
// Query with a SPARQL string
val results = repo.select(SparqlSelectQuery("""
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name WHERE { ?person a foaf:Person ; foaf:name ?name }
"""))
results.forEach { row -> println(row.get("name")) }
Requires kastor-gen-runtime and the KSP processor (kastor-gen-processor) to generate the implementation.
import com.geoknoesis.kastor.gen.annotations.Rdf // the annotation (not the Rdf factory object)
import com.geoknoesis.kastor.gen.runtime.materialize
import com.geoknoesis.kastor.gen.runtime.materializeIn
import com.geoknoesis.kastor.rdf.iri
// Define domain interface
@Rdf(iri = "http://xmlns.com/foaf/0.1/Person")
interface Person {
@Rdf(iri = "http://xmlns.com/foaf/0.1/name")
val name: String
@Rdf(iri = "http://xmlns.com/foaf/0.1/age")
val age: Int
}
// Materialize from RDF (idiomatic — reified domain type)
val person: Person = repo.materialize(iri("http://example.org/person"))
// Equivalently: repo.defaultGraph.materialize(iri("http://example.org/person"))
// or: iri("http://example.org/person").materializeIn(repo.defaultGraph)
// Use domain object
println("Name: ${person.name}, Age: ${person.age}")
String objects are always literals, and IRIs are explicit. Use qname(...) to expand a prefixed name with declared or built-in prefixes, and iri(...) for full IRIs:
import com.geoknoesis.kastor.rdf.*
import com.geoknoesis.kastor.rdf.vocab.RDF
repo.add {
// Built-in prefixes need no declaration: rdf, rdfs, owl, sh, xsd, obo,
// skos, prov, dcat, dcterms, void, geo, time
prefixes {
put("foaf", "http://xmlns.com/foaf/0.1/") // declare (or override) prefixes
}
prefix("ex", "http://example.org/") // single-prefix form
val person = qname("ex:person")
person - RDF.type - qname("foaf:Person") // → <http://xmlns.com/foaf/0.1/Person>
person - qname("rdfs:label") - "Person" // predicate from a built-in prefix
person - qname("foaf:homepage") - iri("http://example.org/profile")
}
import com.geoknoesis.kastor.rdf.* // Direction, add, iri, lit, string
import com.geoknoesis.kastor.rdf.vocab.FOAF
import com.geoknoesis.kastor.rdf.vocab.XSD
repo.add {
val person = iri("http://example.org/person")
person - FOAF.name - string("foaf:Person") // "foaf:Person"^^xsd:string (never an IRI)
person - FOAF.name - lang("Alice", "en") // "Alice"@en
person - FOAF.name - lang("مرحبا", "ar", Direction.RTL) // RDF 1.2 directional string
person - FOAF.age - lit("30", XSD.integer) // "30"^^xsd:integer
}
The Kotlin samples in this README are compiled by the build; see examples/hello-world/src/main/kotlin/ReadmeSnippets.kt.
Not yet on Maven Central. Kastor artifacts have not been published yet. Build from source and install into your local Maven repository with
./gradlew publishToMavenLocal, then addmavenLocal()to your repositories. You can also include the Kastor checkout as a composite build (includeBuild("../kastor")). The version (0.3.0-SNAPSHOT) is defined once ingradle.properties. Requires JDK 21+ and Kotlin 2.4.
dependencies {
// Align all Kastor modules
implementation(platform("com.geoknoesis.kastor:kastor-bom:0.3.0-SNAPSHOT"))
// Kastor RDF Core (transitive: rdf-sparql-contract for query markers)
implementation("com.geoknoesis.kastor:rdf-core")
// Backends (at least one for SPARQL)
implementation("com.geoknoesis.kastor:rdf-jena")
implementation("com.geoknoesis.kastor:rdf-rdf4j")
// Kastor Gen
implementation("com.geoknoesis.kastor:kastor-gen-runtime")
ksp("com.geoknoesis.kastor:kastor-gen-processor:0.3.0-SNAPSHOT")
// Optional: Kotlin SPARQL query DSL / sparql package / flows
implementation("com.geoknoesis.kastor:rdf-sparql-lang")
// Optional: SHACL shapes DSL (shacl {}, Rdf.shacl); pulls rdf-sparql-lang transitively
implementation("com.geoknoesis.kastor:rdf-shacl-dsl")
// Optional: SHACL validation, reasoning
implementation("com.geoknoesis.kastor:rdf-shacl-validation")
implementation("com.geoknoesis.kastor:rdf-reasoning")
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>kastor-bom</artifactId>
<version>0.3.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-core</artifactId>
</dependency>
<!-- Kotlin SPARQL query DSL (optional) -->
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-sparql-lang</artifactId>
</dependency>
<!-- Kotlin SHACL shapes DSL (optional; transitive rdf-sparql-lang) -->
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>rdf-shacl-dsl</artifactId>
</dependency>
<dependency>
<groupId>com.geoknoesis.kastor</groupId>
<artifactId>kastor-gen-runtime</artifactId>
</dependency>
</dependencies>
Kastor is designed for (though not yet proven in) domains such as:
Kastor's architecture is designed around separation of concerns and pluggable components:
┌─────────────────────────────────────────────────────────────┐
│ Kastor Framework │
├─────────────────────────────────────────────────────────────┤
│ 🎨 Natural Language DSL │ 🛡️ Type Safety │ ⚡ Performance │
├─────────────────────────────────────────────────────────────┤
│ Application Layer │
│ 📊 Domain Objects │ 🔄 Materialization │ 🎯 Code Gen │
├─────────────────────────────────────────────────────────────┤
│ Core API Layer │
│ 📈 Graph Ops │ 🔍 SPARQL │ 🧠 Reasoning │ ✅ Validation │
├─────────────────────────────────────────────────────────────┤
│ Provider Layer (Pluggable) │
│ 💾 Memory │ 🏢 Jena │ 🔧 RDF4J │ 🌐 SPARQL │ 🔌 Custom │
└─────────────────────────────────────────────────────────────┘
Layer 1: RDF Core - For semantic web experts
Layer 2: Kastor Gen - For application developers
git clone https://github.com/geoknoesis/kastor.git
cd kastor
./gradlew assemble
./gradlew publishToMavenLocal # optional: consume 0.3.0-SNAPSHOT from mavenLocal()
./gradlew check -x :rdf:conformance:test
./gradlew conformanceSmokeTest
Requires JDK 21+ and Python 3: check also runs the repository quality gates (verifyQualityGates: complexity ratchet, documentation versions, build pins, README imports). See CONTRIBUTING for the full W3C conformance suites, memory settings, and the release process.
Kastor is developed by GeoKnoesis LLC, a company specializing in semantic web technologies and knowledge engineering.
Kastor is open-source and free to use, but maintaining and evolving it requires ongoing effort.
If Kastor is valuable to you or your organization, your financial support helps ensure:
Ways to support:
For organizations: If you're using Kastor in production or need specific features, consider enterprise sponsorship. This helps prioritize your needs and ensures the project continues to evolve in ways that benefit your use case.
See CHANGELOG.md for the full list of changes between releases.
Kastor is licensed under the Apache License 2.0.
Ready to get started? Check out our Getting Started Guide or explore the examples!