本記事は生成AIと共同で執筆しています。事実関係は可能な範囲で一次情報・実機と照合していますが、誤りが含まれている可能性があります。重要な判断を行う前にご自身でもご確認ください。

Ocean と EDC を並べた回の最後に、「触れていないもの」として他のデータスペースの系統を挙げました。そのうちの一つ、日本の実装を手元に建てました。

結論から

建った       docker compose で 11 コンテナ。データ交換まで通った
動かない箇所  公式手順どおりでは 7 箇所で詰まった
             うち ODS 側の問題と言えるのは 3 件。2 件は私の読み違い、
             1 件は起動を止めない設定漏れ、1 件は原因を特定できていない
EDC との差   認可の問いが違う
             EDC 「あなたは何を提示できるか」(クレデンシャルの中身)
             ODS 「あなたは何と関係づけられているか」(関係タプル)
効いてくる差  許可の取り消しは ODS が圧倒的に簡単。ただし相手の事前登録が要る
             実際、タプルを1本消すと同じリクエストが 200 → 403 に変わった

ODS とは何か

ODS(Open Data Spaces)は、IPA が「国や組織ごとの多様性を尊重する、オープンでスケーラブルな分散データマネジメントの技術コンセプト」と位置づけているものです。その下に、参照アーキテクチャ(ODS-RAM)、技術仕様(ODP)、参照実装の OSS、開発キット(SDK)が並びます。本記事が動かすのは、このうち参照実装と SDK です。

主体は三層に分かれています。

役割主体根拠
主導IPA デジタルアーキテクチャ・デザインセンター(DADC)GitHub org の README に "DADC … serves as the neutral body leading ODS"
資金NEDO「ウラノス・エコシステムの実現のためのデータ連携システム構築・実証事業」(2025〜2027年度)
実装レイヤごとに複数の事業者本記事で動かした L2/L3/SDK 群は NTT データグループ名義。Java のパッケージ名は nedo.ods.svc.*

「ウラノスの実装」と一言で言うと不正確で、政策(経産省)・仕様(IPA DADC)・資金(NEDO)・実装(レイヤごとに複数の事業者)が別々です。

実装の名義はリポジトリごとに分かれています。本記事が動かす L2/L3/SDK 群は NTT データグループ名義ですが、決済(DCS-Payment)と通知(CF-Notifier)は ABtC(Automotive and Battery Traceability Center Association)、L4 のセマンティクス系(SPARQL・Discovery・クローラ)は Intent Exchange, Inc.、L3-PAPL2-video-streaming などは Open Dataspaces 名義です。本記事が後で起動する決済サービスも ABtC 名義で、この org は複数事業者の相乗りです。org のプロフィールにも「特定個社へのベンダーロックインを回避し」と書かれています。

もう一つ、名前について注意が要ります。改称が公表されたのは 2025年10月15日、IPA のプレス発表「データスペースの技術コンセプト『Open Data Spaces』の共同推進を合意」です。同発表の「表2 Open Data Spacesの位置付けに係る新旧対応表」に、技術コンセプト名・アーキテクチャ名(ODS-RAM)・プロトコル名(ODP)の新旧が並べて示されています。略称の ODS・ODS-RAM・ODP 自体は改称前から使われており、変わったのは正式名称のほうです。

参照アーキテクチャの文書でいうと、V1(2025年2月28日公開)が旧名の「ウラノス・エコシステム・データスペーシズ リファレンスアーキテクチャモデル」V2(2026年4月1日公開)が新名称「Open Data Spaces リファレンスアーキテクチャモデル(ODS-RAM)」を載せた最初の RAM 文書です。つまり V2 は改称の契機ではなく、改称後に出た最初の版という位置づけになります。

ただし包含関係は変わっていません。IPA のプレス発表は ODS を「ウラノス・エコシステムにより推進される、オープンで中立的な技術コンセプト」と位置づけており、ODS-RAM V2 自体も NEDO のウラノス実証事業の成果を基に編纂されたと明記しています。つまり ウラノス ⊃ ODS のままで、変わったのは中の技術層の呼び名が、国際相互運用性のために中立的な名前に付け替えられたことだけです。

コードは github.com/open-dataspaces にあり、20リポジトリのうち、プロフィール用の .github を除く19件がすべて MIT です。層で分かれています。

L2 トランザクション層   Web API 転送モジュール、映像ストリーミング
L3 アイデンティティ層   アイデンティティコンポーネント、ポリシー管理(PAP)
L4 セマンティクス層     SPARQL サービス、Discovery、クローラ、LLM チャット
SDK                    オンボーディング用(docker compose / Helm / クライアントライブラリ)
その他                  精算・課金/決済、通知

L4 のセマンティクス層は EDC に存在しない層です。ここは後で効いてきます。

建て方

SDK-docker-compose が入口です。構成要素のソースを3つ並べて置き、compose を上げます。

git clone https://github.com/open-dataspaces/SDK-docker-compose.git ods-sdk
cd ods-sdk
git clone --branch=v1.0.0 --depth=1 https://github.com/open-dataspaces/L2-dp-webapi.git
git clone --branch=v1.0.0 --depth=1 https://github.com/open-dataspaces/L3-identity-component.git
git clone --branch=v1.0.0 --depth=1 https://github.com/open-dataspaces/DCS-Payment.git
docker network create shared-network-ods
docker compose up -d

Maven ビルドが2本走るので、初回は時間がかかります。立ち上がるのは11コンテナです。ただしコールドスタートの1回目では揃いません。ログドライバ(Fluentd)がまだ起動していないため gateway が失敗します。もう一度同じコマンドを打つと通ります。

以降に出てくるポート番号は、私の環境の値です。手元で動いている別のものと衝突したので、リポジトリには手を入れず docker-compose.override.yml で付け替えました。ODS の既定値は別です。読み替えてください。

用途本記事ODS の既定
ゲートウェイ81908090
決済 API84018001
OpenFGA Playground33003000
MinIO コンソール95019001
サービス
L2gateway(Spring Cloud Gateway ベースの Web API 転送)
L3l3-app(認証コンポーネント)、keycloak 26.2.5、openfga(AuthZEN 版)
決済payment-apppayment-db
基盤PostgreSQL ×2、MinIO、Fluentd、Mockoon

公式手順どおりでは詰まった箇所が7つ

README の動作確認環境は Windows 11 + WSL2(Ubuntu 24.04) です。macOS で動かすと、順に7箇所で詰まりました。うち 2 件は私の読み違いで、README どおりに進めていれば止まりませんでした(後述の 5・6)。ここではまず起動に関わる4件を挙げ、残りはデータ交換と GUI の節で扱います。

先に断っておきます。この記事を公開する前に、7件それぞれが本当に ODS 側の問題なのかを、リポジトリの実物と突き合わせて検証しました。その結果、2件は私の読み違いで、ODS の不具合ではありませんでした。該当箇所はそのように書き直してあります。動かなかった経緯は残しますが、原因の帰属は訂正しました。

起動に関わる4件のうち、2・4 は環境非依存の欠落です。1 は設定の欠落ではありますが、後述のとおり起動は止まりません。3 は移植性の問題で、README が WSL2 を動作確認環境と明示している以上、「環境の違いではない」とまでは言えません。

1. Keycloak の DB ユーザー名が設定されていない

l3/docker-compose.ymlKC_DB_PASSWORD はあるのに KC_DB_USERNAME がありません

訂正(公開後に検証しました)。当初ここには「Keycloak はコンテナの OS ユーザー(root)で接続しようとするため FATAL: role "root" does not exist で落ちる。環境に関係なく必ず起きる」と書いていました。誤りです。

Keycloak 26.2.5 のコンテナの OS ユーザーは root ではなく keycloak(uid=1000)です。KC_DB_USERNAME を与えない場合、PostgreSQL の JDBC ドライバは既定でこの OS ユーザー名を使います。そして l3/docker-compose.ymlPOSTGRES_USERkeycloak なので、偶然一致して起動が通ります

素の Keycloak 26.2.5 を KC_DB_USERNAME なしで同じ構成の PostgreSQL に向けたところ、Keycloak 26.2.5 on JVM ... started in 6.110s で正常に起動し、pg_stat_activity 上の接続ユーザーも keycloak でした。

私が見た FATAL: role "root" does not exist は、docker execpsql-U なしに叩いたときのものでした(docker exec の既定ユーザーが root のため)。Keycloak 起因ではありません。

つまりこれは起動を止める不具合ではなく、明示されていないことによる潜在的な脆さです。POSTGRES_USERkeycloak 以外に変えた瞬間に壊れます。同梱の setup_l3.shpsql-U を明示しているので、スクリプト経路からこのエラーは出ません。

2. postgres:18 のマウント位置が旧規約のまま

決済サービスの DB が postgres:18-alpine を使っているのに、マウントは旧来の /var/lib/postgresql/data を指しています。PostgreSQL 18 の公式イメージは、データをメジャーバージョン別のディレクトリに置く仕様へ変わりました。

Error: in 18+, these Docker images are configured to store database data in a
       format which is compatible with "pg_ctlcluster" ...
       Counter to that, there appears to be PostgreSQL data in:
         /var/lib/postgresql/data (unused mount/volume)

マウント先を /var/lib/postgresql にすれば起動します。

3. セットアップスクリプトが GNU 版の grep と sed を前提にしている

初期設定は setup/setup_l3.shsetup/setup_l2.sh の2本です。ここで2度つまずきます。

grep: invalid option -- P        # BSD grep には -P(PCRE)が無い
sed: 1: "...": extra characters at the end of n command   # BSD sed の -i は構文が違う

厄介なのは、失敗したときに作りかけの OpenFGA ストアをロールバックすることです。何度か実行するうちに Keycloak 側だけ残って状態が食い違い、結局ボリュームごと消してやり直しました。

macOS では GNU 版を入れて PATH を通してから実行します。

brew install grep gnu-sed
cd setup
PATH="/opt/homebrew/opt/grep/libexec/gnubin:/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH" bash setup_l3.sh

4. レルムとストアの束縛タプルが作られない

ここが一番分かりにくい箇所でした。セットアップは OpenFGA に4つのストアを作ります。

ODS-Auth-Admin                              API 認可
ODS Store - Realm Store Binding             レルムとストアの束縛
ODS Store - Operator Plant Authorization    事業者-プラント認可
ODS-USER-STORE                              利用者

ところが束縛用のストアとモデルは作るのに、肝心の束縛タプルを書きません。そのため認可 API がすべて 403 になります。

{"status":403,"title":"Realm 'master' is not bound to store '01KZW97298EDR6CZ9F36EXBJ83'."}

しかも L3 の認可 API 自体がその束縛を要求するので、API 経由では作れません。鶏と卵です。OpenFGA に直接書く必要がありました。

curl -X POST "http://localhost:8083/stores/$BINDING_STORE/write" \
  -H 'Content-Type: application/json' \
  -d '{"writes":{"tuple_keys":[{"user":"realm:master","relation":"bound_to","object":"store:'$API_STORE'"}]},"authorization_model_id":"'$MODEL'"}'

モデル定義(12-create-model-realm-store-binding.json)を読むと、store 型に bound_to という関係があり、そこに realm 型を直接結べる、と書いてあります。書くべきタプルの形はそこから読み取れます。

認証から認可まで通す

ここからは動きます。まずクライアント認証でトークンを取ります。

curl -X POST "http://localhost:8080/auth/token/client" \
  -H 'Content-Type: application/json' -H 'API-Key: API-Key-Sample' \
  -d '{"client_id":"system-auth-sample","client_secret":"'$SECRET'"}'

返るのは Keycloak の JWT です。有効期限は 60 秒(レルム設定の accessTokenLifespan)なので、以降の手順は取得と一続きにしないと、まず間に合いません。一度 401 Invalid or expired token を食らいました。

このトークンには open_system_id というクレームが1つ乗ります。セットアップスクリプトが Keycloak に作るプロトコルマッパーの仕事です。認証は Keycloak、認可は OpenFGA という分業なので、両者を橋渡しする識別子がここで注入されます。EDC がクレデンシャルの中身そのものを運ぶのとは対照的で、ODS の JWT が運ぶのは識別子だけ、権限は名簿を引いて決まります。

次に事業者を登録します。ODS ではデータを共有する主体をあらかじめ登録するのが前提です。

curl -X POST "http://localhost:8080/account/operator" \
  -H 'Content-Type: application/json' -H 'API-Key: API-Key-Sample' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"login_user_id":"utokyo_test","operator_name":"東京大学(検証用)", ...}'
{"status":201,"data":{"operator_id":"23447708-5d13-430c-834a-06a696897f86", ...}}

そしてこの事業者に権限をタプルとして書きます

curl -X POST "http://localhost:8080/authz/stores/$STORE/write" ... -d '{
  "writes": {"tuple_keys": [
    {"user":"user:23447708-...","relation":"member","object":"role:authz-tuples-admin"}
  ]}}'

最後に、判定が実際に分かれるかを確かめます。

登録した事業者 → authz-tuples-admin : True
登録した事業者 → authz-stores-admin : True
無関係な利用者 → authz-tuples-admin : False

登録した相手だけが通り、それ以外は通らない。ここまでが実機で確認できた範囲です。

データ交換を通す

認可の判定が効くことは確かめました。次はゲートウェイ越しに実際のリクエストを流します。ここでさらに2箇所つまずきます。

リクエストが通る経路の図。クライアントが client credentials で要求し、Keycloak がトークンを発行し、ゲートウェイの認可フィルタを経て転送先エンドポイントへ届く。ゲートウェイからは OpenFGA へ「関係があるか」の問い合わせが下りており、そこでは主体としてトークンの operator_id クレームが、資源としてルートに書いた endpointId が使われる。関係があれば転送、なければ 403 になる。左側には「ここで2回つまずいた(いずれも私の読み違い)」として、⑤ ルートに endpointId が無いと認可フィルタが落ちて 500 になること、⑥ 主体は X-ODS-UserId ヘッダではないことが示されている。下部に設定キー resource-id-metadata-key と subject-id-attribute-name が併記され、「手順書ではなく application.yml を読んで、ようやく分かった」と注記されている。

5. ルートに endpointId が要る(README ではなく、同梱スクリプトに無い)

訂正: この節は当初「README の例には無い」と書いていました。誤りですREADME.md のルート登録例には "metadata": { "endpointId": "test.post" } が含まれており、その意味の解説まで付いています。私が実際に使ったのは README の例ではなく、同梱の補助スクリプト setup/l2route/create_route_mock.sh で、そちらに metadata がありません。詰まったのは事実ですが、原因は README の不備ではありませんでした。

同梱の setup/l2route/create_route_mock.sh が登録するルートは、こういう形です。これをそのまま使うと、リクエストが 500 になります。

curl -X POST -H "X-API-KEY: your-secret-management-api-key" \
  -d '{"id":"route00","uri":"http://mockoon:4011/test",
       "predicates":[{"name":"Path","args":{"_genkey_0":"/test**"}}]}' \
  http://localhost:8190/actuator/gateway/routes/route00

ゲートウェイのログを見ると、認可フィルタが例外で落ちています。

IllegalArgumentException: Gateway route metadata is missing or empty,
so cannot build resource of authorization request for AuthZEN.
  at AuthzenAuthorizationGlobalFilter.buildResource(...:219)

ソースを読むと、application.yml に答えがありました。

resource-id-metadata-key: "endpointId"   # 認可のリソースIDをルートのどのメタデータから取るか

ルートに metadata.endpointId を付けないと、認可要求そのものが組み立てられません。

{"id":"route00","uri":"http://mockoon:4011/test",
 "predicates":[{"name":"Path","args":{"_genkey_0":"/test**"}}],
 "metadata":{"endpointId":"endpoint-test"}}

これを足すと 500 が 403 に変わります。認可の仕組みが動き始めた合図です。

6. 認可の主体は X-ODS-UserId ではない(これは私の読み違い)

訂正: これを ODS 側の問題として数えていましたが、私の誤りですREADME.mdX-ODS-xxx を「ロギング対象項目」と明記しており、認可の主体だとはどこにも書いていません。さらに同じ README は、タプルを user:$OPERATOR_ID の形で書けと明示しています。読み落としたのは私のほうでした。以下は、間違えた経緯としてそのまま残します。

403 のままなので、誰を主体として判定しているのかを調べました。同じ application.yml にあります。

subject-id-attribute-name: "operator_id"   # JWT のどのクレームを主体IDとするか

主体はヘッダの X-ODS-UserId ではなく、JWT の operator_id クレームです。事業者クライアントを発行すると、Keycloak 側にそのクレームを注入するマッパーが自動で作られます。

operator_id      claim=operator_id      value=23447708-5d13-430c-834a-06a696897f86
open_system_id   claim=open_system_id   value=utokyo_test_open_system_id

つまり、「どの事業者が」で判定していて、ヘッダのユーザ ID は別の用途です。ここを取り違えると、いつまでも 403 が続きます。

通った

正しい主体でタプルを1本書きます。

curl -X POST "http://localhost:8083/stores/$USER_STORE/write" \
  -H 'Content-Type: application/json' \
  -d '{"writes":{"tuple_keys":[{"user":"user:23447708-…","relation":"can_access","object":"endpoint:endpoint-test"}]},
       "authorization_model_id":"'$MODEL'"}'

同じリクエストを投げ直すと通ります。

HTTP 200
{"message": "Request successfully delivered!"}

タプル1本で、許可が消えて戻る

ここが今回いちばん確かめたかったところです。同じリクエストを3回、タプルの有無だけ変えて投げます。

同じ要求を3回投げた結果を並べた図。1回目は「関係あり(台帳に線が引かれている)」で 200 と Request successfully delivered!、2回目は「1本削除(線を消す)」で 403 と Access denied、3回目は「書き戻し(線を引き直す)」で再び 200 と Request successfully delivered!。要求の中身は3回とも同一で、変えたのは関係の有無だけ。下部に「失効に必要なのは delete が1回。再発行も失効リストも要らない」と注記されている。

タプルあり : 200  {"message": "Request successfully delivered!"}
  → タプルを1本削除
タプルなし : 403  {"code":"[auth] Forbidden","message":"Access denied"}
  → タプルを書き戻し
復活後     : 200  {"message": "Request successfully delivered!"}

削除1回で権限が消え、書き戻すと復活します。再発行も失効リストも要りません。クレデンシャルを配る方式との違いが、いちばんはっきり出る場面です。

GUI はあるのか

コマンドラインだけの印象がありますが、画面も付いてきます。ただし ODS 自身が作ったものではなく、構成要素が持っているものです。

GUIURLログイン
Keycloak 管理コンソールhttp://auth.localhost:8082/admin/master/console/admin / password
決済 API の Swagger UIhttp://localhost:8401/docs不要
MinIO コンソールhttp://localhost:9501設定した資格情報
OpenFGA Playgroundhttp://localhost:3300/playground不要(ただし後述)

ホスト名に注意が要りますlocalhost:8082 で開くと 302 で auth.localhost:8082 に飛ばされます。compose が KC_HOSTNAMEhttp://auth.localhost:8082 に設定しているためで、最初から後者で開くのが確実です(*.localhost はループバックに解決されるので hosts の追記は不要)。

Keycloak の管理コンソールが一番役に立ちます。セットアップスクリプトが何を作ったのか — レルム、system-auth-sample クライアント、open_system_id を注入するプロトコルマッパー — が画面で確認できます。スクリプトが途中で失敗したときに、どこまで進んだかを見るのにも要ります。

7. Playground は付いてくるが、中身が見えない

OpenFGA には認可モデルとタプルを対話的に確かめる Playground が同梱されていて、ODS もポートを公開しています。ところが開くと、いきなり「Create store」を求められます。

OpenFGA Playground を開いたところ。既存のストアが一覧されず、新規作成のダイアログだけが出る

4つのストアが実在するのに、Playground からは1つも見えない

訂正: 当初ここに「原因はポートの不一致で、Playground は同じオリジンに API がある前提で動く」と書いていました。誤りです。Playground が返す HTML の中身を見ると、iframe の srcfga_api_host=127.0.0.1%3a8083 が入っており、API のポートは正しく 8083 と認識されています

http://localhost:3300/playground → 200
   └ 中身は https://play.fga.dev/sandbox/?fga_api_host=127.0.0.1%3a8083&… への iframe 1行
http://localhost:8083/stores     → 200   (API 本体。4ストアが返る)

同梱の Playground は、HTTPS の play.fga.dev を iframe で読み込み、そこから手元の http://127.0.0.1:8083 を呼ぶ構造です。ブラウザが混在コンテンツとして遮断していると考えるのが自然ですが、コンソールを確認していないので断定はしません。分かっているのは「4つのストアが実在するのに、Playground からは見えない」という結果だけです。

いずれにせよ、タプルを目で確かめたい場合は API を直接叩くほうが早いです。

curl -s http://localhost:8083/stores | jq '.stores[].name'

EDC と何が違うのか

前回 EDC の Minimum Viable Dataspace を動かしたときと並べると、差がはっきりします。

その前に、比較の土台を確認しておきます。そもそも両者は、別のプロトコルの実装です。

EDC は Eclipse / IDSA 系の Dataspace Protocol(DSP) の実装です。DSP には適合性試験(Dataspace TCK)があり、メタデータ・カタログ・契約交渉・転送の 4 領域で仕様の記述を検査します。

一方 ODS は、ODS-RAM V2 に基づく独自のプロトコル群 ODP(Open Data Spaces Protocols) を定義しています。そして、公式ドキュメント全文にも、手元に clone した参照実装一式にも、DSP・IDSA・Catena-X・Gaia-X への言及は 1 件もありませんeclipse という語が出てくるのは、L4 の JSON-LD context の urn:samm:org.eclipse.esmf と、Java イメージの eclipse-temurin だけです)。DSP TCK に相当する公開の適合性試験も見当たりませんでした。

素のままでは繋がらないことは、実例でも裏づけられます。2025 年 3 月に IPA と Catena-X が公表した相互運用の実証は、「認証」「プロトコル」「データモデル」の技術差を特定したうえで、中間レイヤを置いて双方に手を入れずにデータを交換した、という形でした。

以下で比べる認可モデルの差は、この「プロトコルが別物」という前提の上にあります。

認可の問いが違う

EDC と ODS の認可の流れを左右に並べた図。左の Eclipse EDC は「何を提示できるか」を問い、要求する側が資格証明を提示し、ポリシーの評価式が提示された値を読んで判定し、許可か拒否になる。失効するには発行者の失効 API を叩き失効リストを配る仕組みがあり、参照実装に同梱されているが未実行だと注記されている。右の ODS は「何と関係づけられているか」を問い、主体と資源を結ぶ関係をあらかじめ登録しておき、OpenFGA にその線が引けるかを問い合わせ、許可か拒否になる。失効するには線を1本消すだけでよく、実測で確認したとある。下部に「提示物を見るのか、関係を見るのか。相互運用ではここを写像できるかが問題になる」と書かれている。

EDC  「あなたは何を提示できるか」
      提示された検証可能クレデンシャルの中身を、ODRL のポリシーとその評価器で走査する
      (手元の MVD 0.17.0 は実験的な CEL 式を組み込んだ構成)
      例: ManufacturerCredential の part_types が 'all' か

ODS  「あなたは何と関係づけられているか」
      関係タプルのグラフを辿って判定する
      例: user:23447708… と role:authz-tuples-admin の間に member 関係があるか

前者は持参した証明書を読む方式、後者は名簿を引く方式です。OpenFGA は Google の Zanzibar 系で、もともと「誰がどのドキュメントを編集できるか」を大規模に解くための仕組みです。

効いてくるのは「取り消し」と「事前登録」

EDCODS
権限を与える発行者がクレデンシャルを発行する管理者がタプルを書く
権限を取り消す発行者の失効 API を叩くPOST .../credentials/{id}/revoke)。MVD には revoke / suspend / resume / status の 4 本が同梱され、失効リスト配信用のポート(9999 /statuslist)も立つ。ただし README のウォークスルーに失効の筋書きは無く、私は実行していないタプルを1行消すだけ
相手を事前に知る必要発行者を事前に知る必要がある。相手そのものは事前に知らなくてよい相手を事前に知る必要がある。事業者として登録されていないと始まらない
登録の単位発行者を信頼リストに載せる。保持者は都度でよい参加者ごとに登録する

アーカイブの実務から見ると、この対比は他人事ではありません。「一度出した許可を取り消す」は現実に起きます — 寄贈者からの申し出、権利者の異議、不適切な利用の発覚。

どちらにも手段はありますが、性質が 3 点で違います。

反映の速さ   ODS  タプルを消せば次の判定から効く
             EDC  失効リストを取りに行き、検証する経路を通る
粒度         ODS  主体 × 資源の 1 関係だけを外せる
             EDC  クレデンシャル単位。1 枚に複数の資格が乗っていれば巻き添えになる
誰が握るか   ODS  資源側の管理者が消せる
             EDC  失効の権限は発行者にある。利用させる側が単独では止められない

3 つ目がアーカイブには効きます。「この資料を、この人に見せるのをやめる」を所蔵館の判断だけで実行できるかという問いだからです。

一方で ODS は参加者の事前登録が要ります。閲覧室の利用者のように事前登録が前提の場面では痛くありませんが、オンライン公開資料の閲覧者や他機関経由の利用者まで含めると、そうとも限りません。

そのほかの違い

  • セマンティクス層:ODS は L4(SPARQL・Discovery・クローラ)を層として抱えています。ただし「EDC に無い」とは言えません。EDC 側にも FederatedCatalog があり、参加者を巡回するクローラでカタログを集約します。DSP 自体も DCAT と ODRL の上に構築されています。違うのは有無ではなく、RDF/SPARQL をどこまで正面に据えるかで、文化資源のメタデータとは相性が良い層です
  • 決済:ODS は精算・課金/決済サービスを同梱。EDC は対象外で外部に委ねる
  • 起動の重さ:EDC の MVD は Kubernetes 必須、ODS は docker compose で済む
  • 初期化:MVD は seed ジョブが自動で走る。ODS は人がスクリプトを2本実行する

共通していたこと

両方とも、公式手順どおりでは動きませんでした。MVD は事前ビルド済みイメージが非公開で、main がビルドできず、Gateway API のバージョンが噛み合わなかった。ODS は起動に関わる4件。

どちらも「参照実装」であって製品ではない、ということだと思います。動かすたびに、仕様書には書かれていないことが分かります。

アーカイブにとっての含意

EDC 側でアーカイブの利用許諾を ODRL とポリシー評価式で書いてみたとき、判定の軸は3つ、門は2段になりました。同じものを ODS 側で書くと、まったく別の形になります。

EDC  ReadingClearanceCredential.level = restricted   (証明書の中身を見る)
ODS  user:<利用者> --can_read--> holding:<資料>       (関係を書く)

そして ODS 側では、閲覧許可の取り消しが delete 1回で済みます。これは実務的にかなり大きい。

ただし ODS 側で「目録には出るが閲覧はできない」を表現するには、関係の型を分けて設計する必要があります(can_seecan_read を別の関係にする、など)。EDC のようにカタログと契約という2段の門が最初から用意されているわけではありません

触れていないもの

  • L4 セマンティクス層。SPARQL サービスと Discovery は起動していません
  • ODS と EDC の相互接続。それぞれ別々に動かした結果です
  • ODS のプロトコル(ODP)の詳細。仕様書は GitBook にありますが、本記事は実装を動かした記録です

出典

本記事は公開後に、複数エージェントによるファクトチェック(一次資料照合・実測・整合・裁定)を経て修正しています。とくに「EDC では失効が未解決」「KC_DB_USERNAME が無いと必ず起動に失敗する」という当初の記述は、いずれも実機で反証されたため書き直しました。