Skip to content

Testing collaboration on one machine

How to run two Protégé instances against a local server and exercise the whole feature. No Docker, no backend, no database — the bridge runs on its own and tokens can be minted by hand.


Before you start: the ontology needs an absolute IRI

Collaboration cannot work on a project whose ontology IRI is a relative string. Protégé resolves a relative rdf:about against the file's own location on save, so an ontology declared as

<owl:Ontology rdf:about="o"/>

becomes something like file:/C:/Users/you/projects/thing/src/ontology/o the first time it is saved. Every term minted afterwards is then named after a folder on one particular computer, so two people mint different IRIs for the same concept and never converge - which makes such a project precisely the wrong thing to collaborate on.

The wizard rejects a relative base IRI for this reason, so a project generated by OntoBoard today cannot repeat it. To repair an older one, either generate it again with Tools → New ODK Project… and an absolute base IRI (http://purl.obolibrary.org/obo/<id>.owl, or your own domain such as https://w3id.org/<id>), or, to keep the folder: set the ontology IRI to an absolute http IRI in the Active ontology tab, save, then make uribase: in <id>-odk.yaml and the Prefix(...) lines in <id>-idranges.owl match. Entities already minted under the bad IRI have to be renamed; if there are none, there is nothing to migrate.

A stale entry in the sidecar needs no attention - it is pruned automatically, with a line in the log saying so.


A note on what this document used to claim

Everything under Editing below says an edit in one window reaches the other. Until OntoBoard 1.50.0 none of it did, and this document asserted it anyway.

Live collaboration was inert in every earlier release: collab/server.mjs resolved a board by calling openDirectConnection on the Server wrapper instead of the Hocuspocus instance it holds, so the bridge refused every connection with could not open board and no Protégé peer could join. Two further defects sat behind it - a rename never reached a peer, and the different-ontology warning could never fire.

Anyone who followed these steps would have got as far as step 4 and seen a refusal. That nobody reported it is the evidence that nobody did.

The steps below are now executed by two test suites on every build - CollabLiveTest from the Java side and collab/__tests__/server-boot.test.mjs from the JavaScript side - and the pizza project's reports/v5/collaboration.md is produced by opening a socket, so it and this document cannot drift apart without one of them failing.


1. Start the bridge

Only the collaboration service is needed. It stores no ontology, so it needs no database.

cd collab
npm install                      # once

SECRET_KEY=local-test-secret BRIDGE_PORT=1235 COLLAB_PORT=1234 node server.mjs

You should see:

[collab] Hocuspocus server running on port 1234
[collab] JSON bridge for non-JS clients on port 1235

Leave it running. It prints a line each time somebody joins or leaves, which is the quickest way to tell whether a connection got as far as authenticating.

2. Mint two tokens

The bridge only checks that a token is signed with SECRET_KEY and takes the user's name from its sub claim. The backend is not involved, so two tokens can be made directly:

cd collab
SECRET_KEY=local-test-secret npm run --silent mint-token -- alice
SECRET_KEY=local-test-secret npm run --silent mint-token -- bob

Keep both. The name beside a cursor comes from sub, not from anything you type in Protégé — the bridge sets it once at connect from the authenticated user and ignores what a client claims afterwards. Two tokens with the same sub would give you two cursors both labelled the same.

The default is 30 days; --days 1 is plenty for an afternoon of testing.

3. Start two Protégé instances

Launch Protege.exe twice. Two instances run happily side by side, with two caveats:

  • They share ~/.Protege. Preferences and the log are common, so the second instance to save collaboration settings overwrites the first's stored copy. It does not matter while both are running — each holds its own settings in memory — but it means the colour you pick in the second window is the one that persists.
  • They share the log file. ~/.Protege/logs/protege.log interleaves both, so timestamps are how you tell them apart.

Open the same project in both - the pizza project this repository builds end to end is a good one: pizza-edit.owl. Both instances editing one file on disk is fine for this test, because neither writes until you save — but do not save from both, or the second save overwrites the first. The point of the test is that changes travel through the bridge, not through the file.

4. Connect both

In each window, on the OntoBoard tab (Window → Tabs → OntoBoard), press Collaborate… on the board's toolbar.

Field Window A Window B
Server address ws://127.0.0.1:1235 ws://127.0.0.1:1235
Board id pizza-test pizza-test
Access token alice's token bob's token
Your colour pick one pick a different one

Leave Remember the token unticked — it is a credential, and for a test there is no reason to write it to the registry.

Both status bars should read Collaborating as alice on pizza-test and Collaborating as bob on pizza-test. The bridge log should show two joins - [bridge] alice joined 'pizza-test'. If it shows a refusal instead, the message names the reason; an unauthenticated peer is refused rather than admitted as nobody, deliberately.

One account is fine. Two windows using alice's token for both now see each other — echo is filtered per connection rather than per account. You will just have two cursors labelled alice, which makes the test harder to read, so two names are better.


5. What to test, and what to expect

Work down this list. The right-hand column is honest about what is not built yet — a blank result there is not a bug.

Cursors and presence

Try this Expect
Move the mouse over A's canvas A cursor labelled alice moves on B's canvas
Stop moving for 30 seconds The cursor stays — a heartbeat re-sends it every 3s
Select a class in A's tree A ring in alice's colour appears round that node on B
Close A alice's cursor disappears from B within ten seconds
Stop the bridge Both status bars say Disconnected …; retrying in Ns, cursors vanish
Restart the bridge Both reconnect on their own

Editing

Add a class in A and watch B. Everything here goes through Protégé's OWLModelManager, so it also appears in B's class hierarchy, not just on the canvas.

Edit in A Reaches B Notes
New class (double-click the canvas) yes With its position
New class (Protégé's own + button) yes Published from the change listener, so any editor counts
Rename via rdfs:label yes Broken until 1.50.0: a label travels as a nested field and the inbound reader flattened it to a string, so the operation arrived and did nothing
Drag a class from the hierarchy onto the canvas no Canvas membership is per-person by design; the entity itself already exists on both
Draw a relation (existential) yes The reading is preserved — only stays only
Subclass axiom yes
New individual, and its type yes
Delete a class yes Also retracts axioms mentioning it, as Protégé's own delete does
Delete an individual no The shared vocabulary has no removeIndividual; the status bar will say 1 change not shared
Equivalent classes, disjointness, owl:hasKey, domain/range, property characteristics no Counted in the status bar with the reason in its tooltip
Undo (Ctrl+Z) in A yes Undo produces real changes, so they publish like any other

Watch the status bar. N changes not shared is the feature working correctly, not failing — hover it for which axiom kind it was.

Not built yet

These are in the plan, not in the build. Testing them will find nothing:

  • Threaded comments and @mentions — no reply threads and no comment operation in the shared vocabulary. Terms → Discussion link… points at an issue tracker instead, which is where an OBO project's discussion usually lives anyway.
  • Sticky notes and frames, live — they are built and drawn: right-click the canvas for Add note or Add frame, and both are editable in place. What they do not do is travel through the live session. They live in the sidecar rather than in the ontology - deliberately, since a diagram annotation is not an axiom and has no business in a release - so they reach a colleague when the sidecar is committed, not as you type. An inbound addStickyNote from a web client is logged and ignored, which is why that log line exists.
  • Entity locking — advisory locking is in neither the plugin nor the web app.

Three items that used to be on this list now ship, and testing them will find something:

  • Editorial notes — Terms → Note on the selected term… and Terms → All notes… write and list IAO:0000116 editor notes and IAO:0000232 curator notes. They travel live, as updateAnnotation.
  • Provenance — OntoBoard → Record who edits terms… stamps dcterms:contributor and a date; EditWatcher attributes what you change.
  • Release — Build and release → Release… runs the build and stamps owl:versionIRI, and Build and release → Compare releases… diffs two of them term by term.

What the remaining ones need is in the provenance and release requirements analysis, an internal design document.

Resilience worth checking

Try this Expect
Wrong token invalid token: … and no retry loop — check the bridge log shows one connection
ws://127.0.0.1:1234 Refused before a socket opens, with a message about 1235
Kill the bridge, edit in A, restart the bridge A's edits arrive at B when it reconnects — up to 200 are queued
Different board ids Nothing crosses; no error either, because a board id is free text
Same board id, different ontologies open A warning naming the other person. This is the one worth trying deliberately: nothing local can detect it, and without the warning each of you applies the other's axioms to an unrelated file in silence

6. Reading the log

~/.Protege/logs/protege.log, searching for OntoBoard. Lines worth knowing:

OntoBoard: adopted the layout shipped with this version (…)   tab layout updated after an upgrade
OntoBoard: dropped 1 canvas entry for entities no longer …     stale sidecar entry pruned
OntoBoard: … describes '…' but the open ontology is '…'        sidecar belongs to another ontology
OntoBoard: ignoring addStickyNote(…) - …                       an operation this version cannot apply

The bridge's own log is on its terminal: [bridge] alice joined 'pizza-test'.


7. Tearing down

Ctrl+C the bridge. Nothing persists server-side — the operation log lives in memory only, so the next run starts clean. In Protégé, Collaborate… becomes Disconnect.

If you ticked Remember the token, clear it by opening the dialog and unticking — that erases what was written rather than just not writing again.