# Team sharing

> Publish projects to GitHub, construct share URLs, and manage read-only doc sharing via `ok share` and `ok sharing` commands.

- Repository: sashimikun/open-knowledge
- GitHub: https://github.com/sashimikun/open-knowledge
- Human docs: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e
- Complete Markdown: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e/llms-full.txt

## Source Files

- `docs/content/features/share.mdx`
- `packages/cli/src/commands/share/publish.ts`
- `packages/cli/src/commands/sharing/share.ts`
- `packages/server/src/share/publish.ts`
- `packages/server/src/share/construct-url.ts`
- `packages/server/src/mcp/tools/share-link.ts`
- `packages/cli/src/commands/auth/login.ts`

---

---
title: "Team sharing"
description: "Publish projects to GitHub, construct share URLs, and manage read-only doc sharing via `ok share` and `ok sharing` commands."
---

Open Knowledge team sharing spans two surfaces: **share links** (`https://openknowledge.ai/d/…`) that pin a doc or folder to a GitHub branch for read-only handoff, and **config sharing mode** (`ok config-sharing`) that controls whether `.ok/` artifacts ride in git with content or stay machine-local via `.git/info/exclude`. Publishing a no-remote project to GitHub runs through `ok share publish` (or the editor Publish wizard backed by the same HTTP routes); constructing share URLs requires a running collaboration server and a pushed `github.com` `origin`.

## Share link model

A share link encodes a GitHub blob URL (doc) or tree URL (folder) for the project's current branch. The marketing URL wraps that substrate:

```
https://openknowledge.ai/d/{base64url-payload}
```

The payload is a version byte (`0x01`) followed by the UTF-8 GitHub URL. Recipients land on a splash page, then open the target in the desktop app or clone with `ok clone <owner/repo> -b <branch>`.

```mermaid
sequenceDiagram
  participant Editor as Editor / MCP agent
  participant Server as Collaboration server
  participant Git as Local git mirror
  participant Splash as openknowledge.ai splash

  Editor->>Server: POST /api/share/construct-url
  Server->>Git: read HEAD branch, origin, refs/remotes/origin/*
  Git-->>Server: owner/repo, branch, branch on origin
  Server-->>Editor: shareUrl + sharedUrl + branch
  Editor-->>Recipient: clipboard / message with shareUrl
  Recipient->>Splash: open shareUrl
  Splash-->>Recipient: clone or Open in OpenKnowledge
```

<Note>
Share links are **read-only against the working tree**. Constructing a URL does not commit, push, or `git fetch`. The pinned branch must already exist on `origin` (check `refs/remotes/origin/<branch>` or `packed-refs`).
</Note>

### Preconditions

| Requirement | Failure code |
| --- | --- |
| `origin` remote configured | `no-remote` |
| Named branch checked out (not detached HEAD) | `detached-head` |
| Current branch mirrored on `origin` | `branch-not-on-origin` |
| `origin` host is `github.com` (HTTPS, SSH, or `git://`) | `non-github-remote` |
| Path has no `..`, `.git`, backslashes, or control chars | `invalid-path` |

## Publish a project to GitHub

Use publishing when the project has no `origin` (the editor Share button opens the same wizard). The flow lists eligible owners, validates the repo name, creates the repository via GitHub's API, sets `origin`, and pushes the current HEAD.

<Steps>
<Step title="Authenticate">

Run `ok auth login` (device flow) or ensure `gh` CLI credentials are available. Publish and owner-list commands resolve tokens via `gh` first, then the OK token store.

```bash
ok auth login
```

</Step>
<Step title="List eligible owners">

```bash
ok share owners
ok share owners --json
```

Returns your user account plus orgs where you have `can_create_repository` or admin role.

</Step>
<Step title="Check repository name">

```bash
ok share name-check --owner my-org --name my-wiki
ok share name-check --owner my-org --name my-wiki --json
```

Repo names must be 1–100 chars, match `^[A-Za-z0-9._-]+$`, and not start with `.` or `-`. Owner logins must be 1–39 chars, match `^[A-Za-z0-9-]+$`, and not start or end with `-`.

</Step>
<Step title="Publish">

```bash
ok share publish \
  --owner my-org \
  --name my-wiki \
  --visibility private \
  --project-dir /path/to/project
```

On success, stdout reports the clone URL. With `--json`, the terminal line is `{ "type": "publish", "ownerLogin", "repoName", "cloneUrl", "defaultBranch" }`.

The publish flow also: scaffolds OK content if needed, runs `git init` when no `.git` exists, creates an initial commit when HEAD is missing, and on name conflict retries by fetching an existing repo you own before failing.

</Step>
<Step title="Verify">

```bash
git remote -v
git branch -vv
```

Confirm `origin` points at the new GitHub repo and the default branch was pushed.

</Step>
</Steps>

### Publish error codes

| Code | Typical cause |
| --- | --- |
| `auth-required` | No token; run `ok auth login` |
| `name-conflict` | Repo name taken and not accessible to your account |
| `saml-sso` | Org requires SAML SSO authorization |
| `push-failed` | Network, auth, or remote already exists with wrong URL |
| `init-failed` | Could not scaffold content, init git, or create initial commit |
| `network` | Other GitHub API or transport failure |

<Warning>
If the repo was created but the push failed (auth timeout, network blip), retry only the push — the GitHub-side repo is not recreated. The editor wizard exposes a **Retry push** affordance for this case.
</Warning>

## Construct a share URL

### Editor

Click **Share** in the toolbar to copy a `https://openknowledge.ai/d/…` URL for the focused doc or folder. If there is no `origin`, the Publish wizard runs first; after a successful push, the share URL is copied automatically.

### MCP `share_link`

Requires a running Hocuspocus server (`ok start`). Agents build links but **do not publish** — when `no-remote` is returned, direct the user to the Share wizard or `ok share publish`.

<ParamField body="path" type="string" required>
Content-dir-relative target. Doc paths are extension-less (`.md`/`.mdx` stripped on disk probe). Folder paths name a directory. `""` is the content-root sentinel (folder-only).
</ParamField>

<ParamField body="kind" type="'doc' | 'folder'">
Omit to auto-probe (`.mdx` → `.md` → directory). **Required** when `path` is `""`.
</ParamField>

<ParamField body="cwd" type="string">
Optional project root override.
</ParamField>

<ResponseField name="shareUrl" type="string">
Marketing URL: `https://openknowledge.ai/d/{encoded}`.
</ResponseField>

<ResponseField name="sharedUrl" type="string">
Raw GitHub blob or tree URL pinned to `branch`.
</ResponseField>

<ResponseField name="branch" type="string">
Branch encoded in the link (current HEAD).
</ResponseField>

<ResponseField name="resolvedKind" type="'doc' | 'folder'">
Kind the target resolved to after disk probe.
</ResponseField>

<RequestExample>

```json
{ "path": "guides/onboarding", "kind": "doc" }
```

</RequestExample>

<ResponseExample>

```json
{
  "ok": true,
  "shareUrl": "https://openknowledge.ai/d/AbCdEf...",
  "sharedUrl": "https://github.com/my-org/my-wiki/blob/main/guides/onboarding.md",
  "branch": "main",
  "resolvedKind": "doc"
}
```

</ResponseExample>

### HTTP API

:::endpoint POST /api/share/construct-url
Build a share URL from git context. Body is a discriminated union on `kind`.

**Doc request**

```json
{ "kind": "doc", "docPath": "guides/onboarding.md" }
```

**Folder request**

```json
{ "kind": "folder", "folderPath": "guides" }
```

Empty `folderPath` shares the content root (mapped to `tree/<branch>/<content.dir>` when `content.dir` is a subdirectory).

**Success (200)**

```json
{
  "ok": true,
  "shareUrl": "https://openknowledge.ai/d/...",
  "sharedUrl": "https://github.com/owner/repo/blob/main/guides/onboarding.md",
  "branch": "main"
}
```

**Failure (200, `ok: false`)**

```json
{ "ok": false, "error": "branch-not-on-origin", "branch": "feature-x" }
```

GET returns `405`. Malformed bodies return `400`.
:::

| Route | Method | Purpose |
| --- | --- | --- |
| `/api/share/publish/owners` | GET | List publish-eligible owners (spawns `ok share owners`) |
| `/api/share/publish/name-check` | GET | `?owner=&name=` availability check |
| `/api/share/publish` | POST | Create repo, set `origin`, initial push |

<ParamField body="owner" type="string" required>
GitHub user or org login.
</ParamField>

<ParamField body="name" type="string" required>
Repository name.
</ParamField>

<ParamField body="visibility" type="'public' | 'private'" required>
Repository visibility.
</ParamField>

<ParamField body="description" type="string">
Optional repository description.
</ParamField>

## Config sharing mode (`ok config-sharing`)

Separate from share links, **config sharing mode** decides whether Open Knowledge artifacts are committed with project content or hidden per-clone in `.git/info/exclude`.

| Mode | `.git/info/exclude` | Teammates see |
| --- | --- | --- |
| `shared` | OK paths **removed** from exclude | `.ok/`, editor MCP configs, project skills, `.okignore`, `.claude/launch.json` when committed |
| `local-only` | OK paths **added** to exclude | Only markdown content (OK config stays on your machine) |

Set the mode at init with `--shared` or `--local-only`, or toggle later:

<CodeGroup>
```bash title="Enable shared mode"
ok config-sharing share
```

```bash title="Switch to local-only"
ok config-sharing unshare
```

```bash title="Inspect current mode"
ok config-sharing status
```
</CodeGroup>

All subcommands accept `--project <dir>` and `--json`.

<Check>
After `ok config-sharing share`, commit the newly visible OK files so teammates receive the same MCP wiring and project skills.
</Check>

### Unshare refusal

`ok config-sharing unshare` refuses when OK artifacts are already tracked upstream. Untrack with `git rm --cached` on the listed paths, then re-run. Switching to local-only affects teammates on the next pull if those files were previously committed.

### Status output

`ok config-sharing status` reports:

- Current mode: `shared`, `local-only`, or `no-git`
- Paths currently in `.git/info/exclude`
- OK paths tracked upstream despite exclude intent

## Receiving a share link

Recipients open the splash page showing target name, `owner/repo`, and branch.

<Tabs>
<Tab title="Desktop (macOS)">

Click **Open in OpenKnowledge**. The app:

1. Matches `owner/repo` against recent projects — silent open on hit.
2. Otherwise prompts clone vs. locate existing folder.
3. On clone, runs `git clone -b <share-branch>` then navigates to the doc, folder, or project root.

If the repo is open on a different branch, a branch-switch dialog appears when the shared target exists on only one branch.

</Tab>
<Tab title="CLI (macOS and Linux)">

Copy from the splash:

```bash
npm install -g @inkeep/open-knowledge
ok clone owner/repo -b share-branch
```

Branch-pinned clone falls back to the default branch if the feature branch was deleted.

</Tab>
</Tabs>

<Info>
Read-only recipients (no push permission) skip the auto-sync prompt. Sync stays disabled until write access is granted.
</Info>

## Troubleshooting

| Symptom | Fix |
| --- | --- |
| `no-remote` on share | Run Publish wizard, `ok share publish`, or `gh repo create` + `git push -u origin <branch>` |
| `branch-not-on-origin` | `git push -u origin <branch>`; if already pushed, `git fetch origin` to refresh local remote refs |
| `detached-head` | `git checkout <branch>` |
| `non-github-remote` | Share links are GitHub-only in v1; migrate `origin` to `github.com` |
| `auth-required` on publish | `ok auth login` or configure `gh auth login` |
| `saml-sso` | Authorize the OAuth app for the org at the SSO URL GitHub returns |
| `name-conflict` | Pick a different `--name` or use an existing repo you own |
| MCP `share_link` server error | Start the server: `ok start` |
| `config-sharing unshare` refused | `git rm --cached` tracked OK paths, then re-run |

```bash
# Diagnose git + server health
ok diagnose health
ok config-sharing status --json
```

## Related pages

<CardGroup>
<Card title="Auth reference" href="/auth-reference">
GitHub OAuth device flow, PAT storage, and token scopes for publish and clone.
</Card>
<Card title="GitHub sync and conflicts" href="/github-sync-and-conflicts">
Auto-sync, push/pull, and merge conflict resolution after sharing.
</Card>
<Card title="Initialize a project" href="/initialize-project">
`ok init` sharing mode prompt and first-time scaffold.
</Card>
<Card title="HTTP API reference" href="/http-api-reference">
Full `/api/share/*` and editor route inventory.
</Card>
<Card title="MCP tools reference" href="/mcp-tools-reference">
`share_link` tool contract and routed-write boundaries.
</Card>
</CardGroup>
