Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 10 additions & 4 deletions .agents/skills/use-ocis-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ protocol-level work.
- Prefer the global `--json` or `--jsonl` output when consuming results. Do not
parse human-readable tables when structured output is available.
- Use read-only discovery commands such as `ls`, `stat`, `search`, `tree`,
`space list`, `share overview`, `trash list`, and admin `list` or `info`
commands to resolve names and IDs before changing anything.
`space list`, `share overview`, `federation connection list`, `trash list`,
and admin `list` or `info` commands to resolve names and IDs before changing
anything.
- Interpret a remote path in the selected Space. Keep local filesystem paths and
remote oCIS paths distinct according to the command help.

Expand All @@ -55,6 +56,9 @@ protocol-level work.
should be reconciled. Run a sync with `--dry-run` first.
- Use `share received` or `share overview` to inspect shares. Never accept a
share automatically.
- Use `federation invite` and `federation connection` to establish and inspect
OCM identity connections. An invitation establishes a connection; sharing a
file or folder is a separate explicit operation.
- Use `trash` for recoverable deletion management and `version` for historical
file versions.
- Use `admin` only when the user explicitly requests administration. A normal
Expand All @@ -77,14 +81,16 @@ shares, disables, or deletes data:
Never add `--yes` merely to avoid a prompt. Use it only when the user has
explicitly authorized that exact operation. Do not silently overwrite files,
empty trash, permanently delete resources, disable or delete accounts, change
roles, accept or decline shares, or execute unreviewed batch input.
roles, accept federation invitations, remove federation connections, accept or
decline shares, or execute unreviewed batch input.

## Protect authentication and secrets

- Let `ocis auth login PROFILE` handle interactive browser login and server-
required MFA. Tell the user when browser interaction is required.
- Never ask the user to paste a password, access token, refresh token, client
secret, keyring record, or TUS resume URL into the conversation.
secret, keyring record, federation invitation token, or TUS resume URL into
the conversation.
- Never inspect, print, export, or copy operating-system keyring contents.
- Never expose credential-bearing environment variables, authorization headers,
local secret storage, or complete diagnostic output that may contain secrets.
Expand Down
13 changes: 13 additions & 0 deletions .agents/skills/use-ocis-cli/references/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ same command.
- Synchronization
- Spaces
- Sharing
- Federation
- Metadata, trash, and versions
- Administration
- CLI utilities and global flags
Expand Down Expand Up @@ -91,6 +92,7 @@ to change their saved default.
| `share roles` | List server-advertised sharing roles for a resource. |
| `share user add` | Grant a user access to a remote resource. |
| `share group add` | Grant a group access to a remote resource. |
| `share federated add/roles` | Grant an accepted OCM user access using a server-advertised federated role. |
| `share list, share ls` | List outgoing shares for a resource. |
| `share overview` | List outgoing and received shares across Spaces. |
| `share received` | List shares received by the current user. |
Expand All @@ -103,6 +105,17 @@ to change their saved default.

Never accept a received share without an explicit user request.

## Federation

| Command | Purpose |
| --- | --- |
| `federation invite create/list/accept` | Establish an OCM connection between users on two federation-enabled servers. |
| `federation connection list/remove` | Inspect or remove accepted remote-user connections. |

An invitation token establishes identity trust; it does not share a resource.
Treat invitation tokens as secrets and never accept one without an explicit
user request.

## Metadata, trash, and versions

| Command | Purpose |
Expand Down
14 changes: 10 additions & 4 deletions .claude/skills/use-ocis-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ protocol-level work.
- Prefer the global `--json` or `--jsonl` output when consuming results. Do not
parse human-readable tables when structured output is available.
- Use read-only discovery commands such as `ls`, `stat`, `search`, `tree`,
`space list`, `share overview`, `trash list`, and admin `list` or `info`
commands to resolve names and IDs before changing anything.
`space list`, `share overview`, `federation connection list`, `trash list`,
and admin `list` or `info` commands to resolve names and IDs before changing
anything.
- Interpret a remote path in the selected Space. Keep local filesystem paths and
remote oCIS paths distinct according to the command help.

Expand All @@ -55,6 +56,9 @@ protocol-level work.
should be reconciled. Run a sync with `--dry-run` first.
- Use `share received` or `share overview` to inspect shares. Never accept a
share automatically.
- Use `federation invite` and `federation connection` to establish and inspect
OCM identity connections. An invitation establishes a connection; sharing a
file or folder is a separate explicit operation.
- Use `trash` for recoverable deletion management and `version` for historical
file versions.
- Use `admin` only when the user explicitly requests administration. A normal
Expand All @@ -77,14 +81,16 @@ shares, disables, or deletes data:
Never add `--yes` merely to avoid a prompt. Use it only when the user has
explicitly authorized that exact operation. Do not silently overwrite files,
empty trash, permanently delete resources, disable or delete accounts, change
roles, accept or decline shares, or execute unreviewed batch input.
roles, accept federation invitations, remove federation connections, accept or
decline shares, or execute unreviewed batch input.

## Protect authentication and secrets

- Let `ocis auth login PROFILE` handle interactive browser login and server-
required MFA. Tell the user when browser interaction is required.
- Never ask the user to paste a password, access token, refresh token, client
secret, keyring record, or TUS resume URL into the conversation.
secret, keyring record, federation invitation token, or TUS resume URL into
the conversation.
- Never inspect, print, export, or copy operating-system keyring contents.
- Never expose credential-bearing environment variables, authorization headers,
local secret storage, or complete diagnostic output that may contain secrets.
Expand Down
13 changes: 13 additions & 0 deletions .claude/skills/use-ocis-cli/references/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ same command.
- Synchronization
- Spaces
- Sharing
- Federation
- Metadata, trash, and versions
- Administration
- CLI utilities and global flags
Expand Down Expand Up @@ -91,6 +92,7 @@ to change their saved default.
| `share roles` | List server-advertised sharing roles for a resource. |
| `share user add` | Grant a user access to a remote resource. |
| `share group add` | Grant a group access to a remote resource. |
| `share federated add/roles` | Grant an accepted OCM user access using a server-advertised federated role. |
| `share list, share ls` | List outgoing shares for a resource. |
| `share overview` | List outgoing and received shares across Spaces. |
| `share received` | List shares received by the current user. |
Expand All @@ -103,6 +105,17 @@ to change their saved default.

Never accept a received share without an explicit user request.

## Federation

| Command | Purpose |
| --- | --- |
| `federation invite create/list/accept` | Establish an OCM connection between users on two federation-enabled servers. |
| `federation connection list/remove` | Inspect or remove accepted remote-user connections. |

An invitation token establishes identity trust; it does not share a resource.
Treat invitation tokens as secrets and never accept one without an explicit
user request.

## Metadata, trash, and versions

| Command | Purpose |
Expand Down
18 changes: 14 additions & 4 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal/
auth/ OIDC protocol implementation
config/ persisted profile model and atomic storage
credentials/ OS credential-service adapter
federation/ ScienceMesh OCM invitation and connection client
graph/ LibreGraph Spaces, directory, and permission client
httpapi/ authenticated retrying HTTP transport
logging/ opt-in diagnostic logging abstraction
Expand Down Expand Up @@ -73,6 +74,9 @@ without starting a subprocess.
protected resumable-upload locations in separate size-bounded entries in
macOS Keychain, Linux Secret Service, or Windows Credential Manager;
no plaintext or legacy-format migration path exists.
- `internal/federation`: create, list, and accept ScienceMesh invitation tokens
and list or remove accepted OCM user connections. It has no profile,
persistence, Cobra, or resource-sharing policy of its own.
- `internal/graph`: discover, create, inspect, update, and control the lifecycle
and membership of Spaces through LibreGraph; list, inspect, and mutate
directory identities allowed by the server; manage direct group membership
Expand Down Expand Up @@ -126,8 +130,8 @@ without starting a subprocess.
scalar custom-property `PROPFIND`/`PROPPATCH` operations.

Protocol-specific behavior belongs in dedicated `internal/auth`,
`internal/graph`, `internal/search`, `internal/sharing`, `internal/trash`,
`internal/versions`, and `internal/webdav` adapters. Recursive local/remote
`internal/federation`, `internal/graph`, `internal/search`, `internal/sharing`,
`internal/trash`, `internal/versions`, and `internal/webdav` adapters. Recursive local/remote
traversal belongs in `internal/transfer`.

Configuration, credentials, protected upload-session storage, named sync jobs,
Expand Down Expand Up @@ -159,6 +163,12 @@ Fast package tests remain Docker-independent.
LibreGraph drive inventory. It ignores the saved default Space unless an
explicit `--space` filter is provided and excludes declined invitations by
default.
- Federation connection removal requires explicit intent in both the Cobra and
application layers. Invitation acceptance is always explicit, provider input
is restricted to an HTTP(S) host and optional port, and resource invitations
resolve only server-returned users of type `Federated` using the exact oCIS
Graph filter. Federated roles are requested from the server separately from
local-user roles.
- Space names and aliases are convenience selectors. Destructive operations
on disabled Spaces use stable IDs.
- Server-advertised permissions and roles are authoritative; the CLI does not
Expand Down Expand Up @@ -225,7 +235,7 @@ Fast package tests remain Docker-independent.
- Cancellation propagates through Cobra contexts, application use cases, HTTP
requests, and transfer workers and maps to exit code 130.
- New behavior requires tests at its narrowest package boundary.
- Core application, authentication, Graph, HTTP transport, search, sharing,
trash, transfer, versions, and WebDAV packages maintain at least 75%
- Core application, authentication, federation, Graph, HTTP transport, search,
sharing, trash, transfer, versions, and WebDAV packages maintain at least 75%
statement coverage.
- Machine-readable output and exit codes are public compatibility contracts.
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build check clean coverage fmt install integration integration-down \
.PHONY: build check clean coverage fmt install uninstall remove integration integration-down \
integration-logs integration-test integration-up lint race release-check \
release release-smoke release-snapshot secrets test vuln

Expand Down Expand Up @@ -26,6 +26,11 @@ install: build
mkdir -p $(HOME)/.local/bin
cp bin/ocis $(HOME)/.local/bin/ocis

uninstall:
rm -f $(HOME)/.local/bin/ocis

remove: uninstall

test:
go test ./...

Expand All @@ -38,7 +43,7 @@ check: fmt

coverage:
go run ./tools/covercheck -min $(COVERAGE_MIN) \
app auth graph httpapi retry search sharing sync trash transfer versions \
app auth federation graph httpapi retry search sharing sync trash transfer versions \
webdav

fmt:
Expand Down
78 changes: 75 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Requires Go 1.26.5 or newer:
make test
make build
make install
make uninstall # alias: make remove
```

The slower black-box compatibility suite starts a disposable, pinned full oCIS
Expand Down Expand Up @@ -1032,9 +1033,9 @@ ocis share remove SHARE_ID
```

Removal prompts for confirmation and accepts `--yes` only for reviewed
automation. `share list` includes user, group, and public-link shares created
by the caller. `share received [REMOTE_PATH]` lists incoming user/group shares
and is not filtered by `--space`. Filter it with `--state accepted`,
automation. `share list` includes user, group, federated, and public-link shares
created by the caller. `share received [REMOTE_PATH]` lists incoming user,
group, and federated shares and is not filtered by `--space`. Filter it with `--state accepted`,
`--state pending`, `--state declined`, or `--state all`. Human output names the
state; JSON and JSONL include both the numeric OCS `state` and readable
`stateName`.
Expand Down Expand Up @@ -1127,6 +1128,77 @@ never stored in the config or OS credential service and is not accepted as a
command-line value. `--dry-run` is available for creation, update, and
revocation. Dry-run output reports that a password would be set but never reads
or prints the secret.

### Federated Open Cloud Mesh sharing

Federated sharing connects users on two different OCM-enabled oCIS servers.
It has two explicit stages: establish a connection once, then share resources
with that accepted remote user. The CLI never accepts an invitation or a
resource share automatically.

On the invitation issuer's server, create an invitation:

```sh
ocis --profile work federation invite create \
--email bob@remote.example \
--description "Share project documents"
ocis --profile work federation invite list
```

Send the returned token to the other user through a trusted channel. On the
recipient's server, accept it while naming the issuer's public host. A full
`http` or `https` URL is also accepted; paths, queries, credentials, and other
URL schemes are rejected:

```sh
ocis --profile remote federation invite accept INVITATION_TOKEN \
--provider cloud.example.com
```

To avoid placing the invitation token in shell history, omit the positional
token and enter it at the secure prompt, or set
`OCIS_FEDERATION_INVITE_TOKEN` for non-interactive execution:

```sh
ocis --profile remote federation invite accept \
--provider cloud.example.com
```

After acceptance, both users can discover the connection and share files or
folders using the server-advertised federated roles:

```sh
ocis federation connection list
ocis --space Engineering share federated roles /reports/report.pdf
ocis --space Engineering share federated add \
/reports/report.pdf bob@remote.example --role viewer --dry-run
ocis --space Engineering share federated add \
/reports/report.pdf bob@remote.example --role viewer
```

Incoming OCM resource shares appear in the existing intentional workflow:

```sh
ocis share received --state pending
ocis share accept SHARE_ID --dry-run
ocis share accept SHARE_ID
```

Remove a federated connection only after reviewing it. Removal can make
resources shared through that connection unavailable:

```sh
ocis federation connection remove bob@remote.example --dry-run
ocis federation connection remove bob@remote.example
```

Both servers must enable incoming and outgoing OCM support. The CLI reads the
server's federation capabilities and returns a conflict error before mutation
when the required direction is disabled. Invitations establish a connection;
they do not themselves grant access to any file or Space. Federated users
cannot be added as project Space members in current oCIS, so share a file or
folder inside the Space instead.

The server remains authoritative for directory visibility, available roles,
sharing restrictions, and resource permissions. A user may be able to read a
file without being allowed to share it, update a share, or remove another
Expand Down
2 changes: 2 additions & 0 deletions internal/app/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const (
ShareLinkInfo ShareOperation = "link-info"
ShareLinkUpdate ShareOperation = "link-update"
ShareDirectAdd ShareOperation = "direct-add"
ShareFederatedAdd ShareOperation = "federated-add"
ShareDirectUpdate ShareOperation = "direct-update"
ShareRemove ShareOperation = "remove"
ShareOverview ShareOperation = "overview"
Expand Down Expand Up @@ -193,6 +194,7 @@ type ShareRequest struct {
LinksOnly bool
Confirmed bool
DryRun bool
Federated bool
}

// TrashOperation identifies a recycle-bin use case.
Expand Down
42 changes: 42 additions & 0 deletions internal/app/federation_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app

import "context"

// FederationOperation identifies an OCM connection-management use case.
type FederationOperation string

const (
FederationInviteCreate FederationOperation = "invite-create"
FederationInviteList FederationOperation = "invite-list"
FederationInviteAccept FederationOperation = "invite-accept"
FederationConnectionList FederationOperation = "connection-list"
FederationConnectionRemove FederationOperation = "connection-remove"
)

// FederationRequest describes one OCM invitation or connection operation.
type FederationRequest struct {
Operation FederationOperation
Token string
Provider string
Email string
Description string
Identifier string
UserID bool
Confirmed bool
DryRun bool
}

// RunFederationWithOptions manages OCM invitations and connections.
func RunFederationWithOptions(
ctx context.Context,
request FederationRequest,
selectedProfile string,
options RunOptions,
) error {
return classifyProtocolError(
"federation "+string(request.Operation),
runFederation(
ctx, request, selectedProfile, options.normalized(),
),
)
}
Loading