Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0600d20
Add first-value dashboard activation layer
KAVentures Sep 27, 2026
fded24d
Harden first-value dashboard injection
KAVentures Sep 27, 2026
37046fd
Add evidence-driven first-value experience
KAVentures Sep 27, 2026
818758e
Load first-value activation dashboard layer
KAVentures Sep 27, 2026
cca86c8
Add first-value compatibility regressions
KAVentures Sep 27, 2026
5e31611
Test first-value dashboard JavaScript contract
KAVentures Sep 27, 2026
f637711
Bump OpenWorkGraph to v0.96.0
KAVentures Sep 27, 2026
6a0d5e7
Align root package version for v0.96.0
KAVentures Sep 27, 2026
971ed44
Describe v0.96 first-value activation in MCP bundle
KAVentures Sep 27, 2026
418fbeb
Align Python agent SDK version for v0.96.0
KAVentures Sep 27, 2026
8f37d0e
Align Node agent SDK version for v0.96.0
KAVentures Sep 27, 2026
ca3db25
Align release contract with v0.96.0
KAVentures Sep 27, 2026
5fc598f
Broaden first-value readiness to focus-only workflows
KAVentures Sep 27, 2026
8ccf592
Describe first-value activation in v0.96 release
KAVentures Sep 28, 2026
4b6d489
Document v0.96 first-value activation
KAVentures Sep 28, 2026
c64dbde
Keep activation polling lightweight and overview-scoped
KAVentures Sep 28, 2026
376461e
Gateway admin accounts, employee roster and the employee's own view
KAVentures Sep 28, 2026
8226792
Merge pull request #100 from KAVentures/feat/first-value-activation-v096
KAVentures Sep 28, 2026
798e0ee
Merge v0.96 activation into Gateway identity admin
KAVentures Sep 28, 2026
8fb99c7
Harden enterprise identity recovery and invitation reuse
KAVentures Sep 28, 2026
3d31c65
Install identity security hardening in enterprise Gateway
KAVentures Sep 28, 2026
2a32418
Test identity recovery and per-device SSO invariants
KAVentures Sep 28, 2026
02fb17e
Prepare Gateway identity release v0.97.0
KAVentures Sep 28, 2026
03246a2
Align root package version to v0.97.0
KAVentures Sep 28, 2026
c5ece32
Align Claude bundle version to v0.97.0
KAVentures Sep 28, 2026
915a22e
Align Python agent SDK version to v0.97.0
KAVentures Sep 28, 2026
91c7aff
Align Node agent SDK version to v0.97.0
KAVentures Sep 28, 2026
0a9d910
Expect v0.97.0 across release version sources
KAVentures Sep 28, 2026
6a28919
Add v0.97 Gateway identity release notes
KAVentures Sep 28, 2026
9d1000a
Make live secure-app integration timeout robust on Windows CI
KAVentures Sep 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ jobs:

OpenWorkGraph is local-first, open-source context infrastructure for how human and AI-agent work actually happens.

### First useful reconstruction
The local dashboard now has an evidence-driven first-value layer designed to become useful during the first real work session. It watches only the existing privacy-hardened local evidence surfaces and, once enough activity exists, offers a deterministic **Your last few minutes** reconstruction. This is a presentation layer over canonical evidence, not a new inference or capture pipeline.

The activation layer adds no sensor, OS/browser permission, AI permission, retention permission, Gateway sharing, MCP tool, screenshot capture, filesystem watcher, prompt/response capture or content telemetry. It never auto-enables AI access or saved history. Empty first-run placeholders are suppressed until their underlying features have useful data, while Evidence, History, Agents, Connect, Organization and Export remain available unchanged.

### Local-first remains the default
A normal OpenWorkGraph install captures to local SQLite, provides local dashboard/export/API/MCP access, and requires no OpenWorkGraph account or OpenWorkGraph-hosted evidence store. Capture continues locally if an optional customer-controlled Gateway or network is unavailable.

Expand All @@ -156,7 +161,7 @@ jobs:
The dashboard now separates giving an AI access to OpenWorkGraph context from observing an agent's own execution. It provides reviewable setup material for Claude Code lifecycle hooks, Codex trace export, OpenAI Agents tracing and generic OpenTelemetry/custom structural adapters. OpenWorkGraph does not silently edit third-party configuration files. An integration is shown as active only when telemetry actually observed by the local evidence store supports that status.

### Custom harnesses
Arbitrary self-built or third-party agent harnesses can now connect in either or both directions. Python and Node/TypeScript helpers, OTLP/HTTP JSON and raw structural HTTP can send privacy-safe execution telemetry through the dedicated write-only agent credential. Any MCP-capable harness can separately read only the OpenWorkGraph context the user has authorized. The setup flow keeps telemetry write permission and context/history read permission explicitly separate.
Arbitrary self-built or third-party agent harnesses can connect in either or both directions. Python and Node/TypeScript helpers, OTLP/HTTP JSON and raw structural HTTP can send privacy-safe execution telemetry through the dedicated write-only agent credential. Any MCP-capable harness can separately read only the OpenWorkGraph context the user has authorized. The setup flow keeps telemetry write permission and context/history read permission explicitly separate.

The standalone helpers do not accept or serialize prompt text, model responses, tool arguments/results, returned values, exception text or hidden reasoning. OpenWorkGraph observer failures remain fail-open for the agent. This release publishes **OpenWorkGraph-Agent-Python.py**, **OpenWorkGraph-Agent-Node.mjs** and **OpenWorkGraph-Agent-Node.d.ts** as standalone release assets.

Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,13 @@ context:read
transfers:read
```

For larger deployments the Gateway can sit behind customer-controlled OAuth/OIDC/SSO infrastructure. Native enterprise identity provisioning is layered separately from the core data plane.
Administrators use named accounts at `https://<your-gateway>/admin`: password plus authenticator app, or company sign-in (OpenID Connect).

- **Roles:** owner, admin or read-only viewer.
- **Employees:** a roster, with personal invitations that tie each computer to one employee.
- **Employee view:** each employee can see what the Gateway holds about them, and every recorded read, at `/me`.

See [docs/ORGANIZATION_ROLLOUT.md](docs/ORGANIZATION_ROLLOUT.md).

---

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.95.0
0.97.0
281 changes: 281 additions & 0 deletions dashboard/first_value_activation.js

Large diffs are not rendered by default.

66 changes: 62 additions & 4 deletions dashboard/org_join.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,50 @@
'<input id="orgJoinCode" class="org-join-field" autocomplete="off" spellcheck="false" placeholder="owgjoin1.…">' +
'<div style="margin-top:10px"><button id="orgJoinPreviewButton" class="secondary">Review what will be shared</button></div>' +
'<div id="orgJoinPreview" hidden style="margin-top:12px"><div><b>Organization:</b> <span id="orgJoinOrg"></span></div><div id="orgJoinShares"></div>' +
'<label for="orgJoinActor" style="display:block;margin-top:8px;font-weight:600">Your work email or username</label>' +
'<input id="orgJoinActor" class="org-join-field" autocomplete="email">' +
'<div id="orgJoinIdentity" hidden style="margin-top:8px"></div>' +
'<div id="orgJoinActorBox"><label for="orgJoinActor" style="display:block;margin-top:8px;font-weight:600">Your work email or username</label>' +
'<input id="orgJoinActor" class="org-join-field" autocomplete="email"></div>' +
'<label style="display:flex;gap:8px;margin-top:10px"><input type="checkbox" id="orgJoinConsent"> I reviewed what will be shared with my organization.</label>' +
'<div style="margin-top:10px"><button id="orgJoinButton">Join</button></div></div>' +
'<div id="orgJoinResult" role="status" aria-live="polite" style="margin-top:8px"></div>';
const gatewayPanel = $('gatewayPanel');
panel.insertBefore(card, gatewayPanel || managedNote.nextSibling);

const meCard = document.createElement('div');
meCard.id = 'orgMeCard';
meCard.className = 'card';
meCard.hidden = true;
meCard.innerHTML = '<h2>What your organization holds about you</h2>' +
'<div class="muted">Open a page on your organization\'s Gateway that shows exactly what it received from your computers, who can read it, and every recorded read. The link is personal, works once and expires in two minutes.</div>' +
'<div style="margin-top:10px"><button id="orgMeButton" class="secondary">See what your organization holds about you</button></div>' +
'<div id="orgMeResult" role="status" aria-live="polite" class="muted" style="margin-top:6px"></div>';
panel.insertBefore(meCard, card);
}
}

async function refreshMe() {
try {
const st = await call('/v1/gateway-status');
const card = $('orgMeCard');
if (card) card.hidden = !st.enrolled;
} catch (_) { /* local-only */ }
}

async function openMe() {
$('orgMeResult').textContent = '';
// Open the tab during the click; browsers block popups opened after an await.
const tab = window.open('', '_blank');
if (tab) tab.opener = null;
try {
const r = await call('/v1/org-me-link', {});
if (tab) { tab.location.replace(r.url); return; }
const a = document.createElement('a');
a.href = r.url; a.target = '_blank'; a.rel = 'noopener noreferrer';
a.textContent = 'Open your page (the link works once, for two minutes)';
$('orgMeResult').replaceChildren(a);
} catch (e) {
if (tab) tab.close();
$('orgMeResult').textContent = e.message;
}
}

Expand Down Expand Up @@ -91,6 +128,22 @@
previewed = await call('/v1/org-join/preview', {join_code: code});
$('orgJoinPreview').hidden = false;
$('orgJoinOrg').textContent = previewed.organization_name;
const id = previewed.identity || {};
const needsConfirm = !!(id.locked && id.require_sso && !id.sso_verified);
$('orgJoinActorBox').hidden = !!id.locked;
$('orgJoinIdentity').hidden = !id.locked;
if (id.locked) {
$('orgJoinIdentity').innerHTML = `<div><b>You will join as:</b> ${esc(id.display_name || id.email)} (${esc(id.email)})</div>` +
'<div class="muted">This invitation is personal: it can only connect computers as this person.</div>' +
(needsConfirm
? '<div style="margin-top:8px"><b>First confirm it\'s you</b> with your company account. <button id="orgJoinConfirm" class="secondary">Confirm with company account</button> <button id="orgJoinRecheck" class="secondary">I confirmed, check again</button></div>'
: (id.require_sso ? '<div style="margin-top:6px">✓ Confirmed with your company account.</div>' : ''));
const c = $('orgJoinConfirm');
if (c) c.addEventListener('click', () => window.open(id.verify_url, '_blank', 'noopener'));
const r = $('orgJoinRecheck');
if (r) r.addEventListener('click', preview);
}
$('orgJoinButton').disabled = needsConfirm;
$('orgJoinShares').innerHTML = sharingList(previewed.sharing) +
'<p class="muted"><b>Never shared:</b> ' + esc((previewed.never_shared || []).join(', ')) + '.</p>' +
'<p class="muted"><b>You can:</b> ' + esc((previewed.you_can || []).join(', ')) + '.</p>';
Expand All @@ -103,8 +156,9 @@

async function join() {
if (!previewed) return;
const actor = $('orgJoinActor').value.trim();
if (!actor) {
const locked = !!(previewed.identity && previewed.identity.locked);
const actor = locked ? '' : $('orgJoinActor').value.trim();
if (!locked && !actor) {
$('orgJoinResult').textContent = 'Enter your work email or username.';
return;
}
Expand All @@ -121,6 +175,7 @@
});
previewed = null;
$('orgJoinCard').innerHTML = `<h2>Joined ${esc(r.organization_name)}</h2><div class="muted">Sharing starts from enrollment forward. Anything recorded before joining stays on this computer. You can pause or disconnect in the Organization section.</div>`;
refreshMe();
if (typeof window.refreshGatewayPanel === 'function') window.refreshGatewayPanel();
} catch (e) {
$('orgJoinResult').textContent = e.message;
Expand Down Expand Up @@ -163,6 +218,9 @@
if (p) p.addEventListener('click', preview);
const j = $('orgJoinButton');
if (j) j.addEventListener('click', join);
const m = $('orgMeButton');
if (m) m.addEventListener('click', openMe);
setTimeout(refreshManaged, 300);
setTimeout(refreshMe, 300);
});
})();
19 changes: 19 additions & 0 deletions deploy/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,22 @@ OWG_GATEWAY_AGGREGATE_MIN_ACTORS=5
# Use a stable random secret >=32 characters; rotation intentionally changes all
# pseudonyms. Pseudonymization is NOT anonymization.
OWG_GATEWAY_PSEUDONYM_KEY=

# The Gateway's public HTTPS address, e.g. https://owg.example.com. Used for
# company sign-in redirects and for invitation links. Required for SSO.
OWG_GATEWAY_PUBLIC_URL=

# Optional company sign-in (OpenID Connect) for administrators (/admin),
# employees (/me) and invitation confirmation (/join/verify). Register the
# redirect URI <OWG_GATEWAY_PUBLIC_URL>/sso/callback with your identity
# provider. The issuer defaults to OWG_GATEWAY_OIDC_ISSUER when left blank.
OWG_GATEWAY_SSO_ISSUER=
OWG_GATEWAY_SSO_CLIENT_ID=
OWG_GATEWAY_SSO_CLIENT_SECRET=
# Optional comma-separated list, e.g. acme.se,acme.com
OWG_GATEWAY_SSO_ALLOWED_DOMAINS=

# OWG_GATEWAY_ADMIN_TOKEN creates the first administrator at /admin. Once your
# named administrators are set up, set this to "disabled" so the shared token
# can no longer call admin APIs (it can still create the first owner if none exists).
OWG_GATEWAY_ADMIN_TOKEN_API=enabled
6 changes: 6 additions & 0 deletions deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ services:
OWG_GATEWAY_OIDC_SELF_READ: ${OWG_GATEWAY_OIDC_SELF_READ:-true}
OWG_GATEWAY_AGGREGATE_MIN_ACTORS: ${OWG_GATEWAY_AGGREGATE_MIN_ACTORS:-5}
OWG_GATEWAY_PSEUDONYM_KEY: ${OWG_GATEWAY_PSEUDONYM_KEY:-}
OWG_GATEWAY_ADMIN_TOKEN_API: ${OWG_GATEWAY_ADMIN_TOKEN_API:-enabled}
OWG_GATEWAY_PUBLIC_URL: ${OWG_GATEWAY_PUBLIC_URL:-}
OWG_GATEWAY_SSO_ISSUER: ${OWG_GATEWAY_SSO_ISSUER:-}
OWG_GATEWAY_SSO_CLIENT_ID: ${OWG_GATEWAY_SSO_CLIENT_ID:-}
OWG_GATEWAY_SSO_CLIENT_SECRET: ${OWG_GATEWAY_SSO_CLIENT_SECRET:-}
OWG_GATEWAY_SSO_ALLOWED_DOMAINS: ${OWG_GATEWAY_SSO_ALLOWED_DOMAINS:-}
ports:
- "${OWG_GATEWAY_BIND_ADDRESS:-127.0.0.1}:${OWG_GATEWAY_PORT:-8790}:8790"

Expand Down
22 changes: 22 additions & 0 deletions docs/CHANGELOG_V096.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# OpenWorkGraph v0.96 — first useful reconstruction

v0.96 adds a thin first-run activation layer over the existing local evidence stack. It is intended to make OpenWorkGraph understandable during the first real work session without changing what the product captures or what an AI may access.

## What changes

- Overview shows a dismissible **See what OpenWorkGraph understands** card.
- Progress is evidence-driven rather than a countdown: observed events, work surfaces, transitions and agent runs.
- Once enough current-session evidence exists, **Your last few minutes** presents a deterministic reconstruction from existing privacy-hardened dashboard evidence and structural agent-run reports.
- If interaction-level evidence is sparse, already-derived observed surface transitions provide a factual fallback rather than inventing task intent.
- The next action is contextual: Connect AI only when the user chooses, optional browser-sensor setup when browser context is shallow, and optional native/OTel agent telemetry when an agent is only surface-observed.
- The old unfinished timeline placeholder and an empty repeated-workflows card are suppressed on first run until they have useful content. Their underlying DOM/data paths remain intact.

## What does not change

v0.96 adds no capture sensor, screenshot capture, filesystem watcher, prompt/response capture, clipboard-content capture, browser/OS permission, database schema, retention rule, AI permission, saved-history lease, Gateway behavior, agent-ingest permission, export format or MCP tool.

The first-value layer performs GET requests only against existing authenticated local endpoints. It cannot enable AI access, save history, synchronize evidence or mutate canonical evidence.

## Compatibility goal

Evidence, History, Agents, Connect, Organization and Export remain the established advanced surfaces. Dismissing the first-value card leaves the ordinary dashboard behavior intact. Existing installations and existing MCP configurations keep their prior semantics.
44 changes: 44 additions & 0 deletions docs/CHANGELOG_V097.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# OpenWorkGraph v0.97 — Gateway identity and employee transparency

v0.97 adds an enterprise identity layer to the optional customer-controlled Gateway while preserving the local-first evidence, v0.96 first-value activation, retention, MCP, agent-ingest and export behavior.

## Named Gateway administrators

- `/admin` supports named **owner**, **admin** and **viewer** accounts instead of requiring every administrator to act anonymously through one shared token.
- Administrators can sign in with password + authenticator code (TOTP), or with company OpenID Connect when configured.
- The first owner is bootstrapped with `OWG_GATEWAY_ADMIN_TOKEN`; subsequent administrators get single-use setup links.
- Sign-in sessions have idle and absolute expiry, repeated failures are throttled/locked, and TOTP time steps cannot be reused.
- The bootstrap token can be disabled for normal admin API use after named accounts exist. If an installation loses its only owner's credentials, deliberately re-enabling/presenting the bootstrap token can reset that owner without weakening the normal last-owner protection.
- Audit rows attribute actions to the named administrator when one is signed in.

## Employee roster and identity-bound enrollment

- Administrators can maintain an employee roster manually or by CSV, including team membership.
- Personal invitations lock enrollment to one roster employee; the computer cannot substitute another actor identity.
- A computer records how its identity was established: SSO-confirmed, personal invitation, linked by an administrator, or self-reported legacy/group enrollment.
- Organizations can require identity-bound personal invitations for all new computers.
- Offboarding revokes the employee's active device credentials and open invitations without silently deleting retained evidence.
- When SSO is required, one successful company-account confirmation authorizes **one** computer enrollment. A multi-device invitation requires a fresh confirmation for each additional computer, so a copied invite cannot reuse an earlier SSO proof.

## Employee `/me` view

- An enrolled employee can open `/me` from their own local OpenWorkGraph using a single-use, two-minute login code minted with the device credential.
- When company sign-in is enabled, `/me` may also use the roster-matching company account.
- The page is scoped to that employee and shows their connected computers, evidence held by the Gateway, organization sharing ceiling, readers with explicit access and recorded reads/changes involving them.
- Viewing evidence through `/me` is itself audited.
- If the same email belongs to more than one organization on a multi-tenant Gateway, generic SSO sign-in refuses to guess; the employee must open `/me` from an enrolled computer (or otherwise provide an organization-specific context).

## Company sign-in

- OpenID Connect authorization-code flow with PKCE (S256), nonce, single-use state, issuer/audience/expiry validation and provider-published signing keys.
- If the provider explicitly sends `email_verified: false`, sign-in is refused. Some enterprise providers omit that optional claim; in that case OWG still requires a cryptographically verified ID token, a usable email/UPN claim, optional allowed-domain match, and a matching known administrator/roster employee for the requested action.
- Optional `OWG_GATEWAY_SSO_ALLOWED_DOMAINS` can restrict accepted company-account domains.
- `OWG_GATEWAY_PUBLIC_URL` supplies the HTTPS redirect origin; Docker/self-host configuration passes the identity settings explicitly.

## Security and compatibility

- Setup/session/invitation secrets used by browser pages travel in URL fragments and are removed from the address bar on arrival; Gateway pages use no external scripts and use CSP/nonces, `no-store` and frame denial.
- Existing admin API endpoints remain available to the bootstrap token until the operator disables that path.
- Existing group invitations, managed enrollment, legacy enrollment and already-enrolled devices continue to work. Their identity is shown as self-reported until linked where appropriate.
- Existing v0.96 first-value activation, History/Retention, MCP contracts, agent telemetry, organization sharing ceilings and canonical evidence formats are preserved.
- No SCIM provisioning is included in this release.
Loading
Loading