Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fc249d7
Add optional rooted filesystem handles and bridge transport
techartdev Sep 10, 2026
19332e3
Integrate optional drive bridge installation and attachment lifecycle
techartdev Sep 10, 2026
fcab81b
Record bridge open-handle fixes and remaining native checks
techartdev Sep 10, 2026
d70778f
Retain drive ownership through explicit cleanup recovery
techartdev Sep 10, 2026
30df44a
Verify Linux mapped files through the native bridge and SFTP
techartdev Sep 10, 2026
5a9cfd9
Record native Windows bridge acceptance and remaining integration checks
techartdev Sep 10, 2026
0a23e68
Retire SFTP channels after unconfirmed mount handle acquisition
techartdev Sep 10, 2026
99314be
Record native Windows connection-loss verification
techartdev Sep 10, 2026
8ae44d5
Verify native Linux transport loss and disconnected mount cleanup
techartdev Sep 10, 2026
e9c036b
Retire mounted filesystems when their original SSH connection closes
techartdev Sep 10, 2026
b2069b3
Detect idle SFTP channel closure in mount heartbeats
techartdev Sep 10, 2026
16ce9e4
Offer available drive letters for local attachments
techartdev Sep 10, 2026
48dafcf
Record native Windows open-directory rename acceptance
techartdev Sep 10, 2026
b1965ef
Verify native directory rewinds through the SFTP filesystem grant
techartdev Sep 10, 2026
d58bd61
Verify unprivileged native FUSE operations and failure cleanup
techartdev Sep 10, 2026
36d0147
Record Windows volume flush failure and recovery verification
techartdev Sep 10, 2026
3169aa7
Verify native SFTP mount identity after unlink and recreate
techartdev Sep 10, 2026
324e742
Allow owner metadata updates through mounted read handles
techartdev Sep 10, 2026
0b9e098
Verify SFTP timestamp updates and rejection without partial effects
techartdev Sep 10, 2026
1d0fe84
Verify SFTP creation attributes and record native copy behavior
techartdev Sep 10, 2026
c491d73
Record native creation and overwrite attribute acceptance
techartdev Sep 10, 2026
27f0b65
Retire unconfirmed SFTP closes and verify native stalled-channel cleanup
techartdev Sep 10, 2026
27afacb
Prepare public release and SDK distribution
techartdev Sep 10, 2026
5102550
Preserve metadata updates through mounted read handles
techartdev Sep 10, 2026
05f9cb8
Declare and enforce app compatibility with the ShellCanvas client pla…
techartdev Sep 10, 2026
0c45ce7
Merge branch 'main' into codex/public-release
techartdev Sep 10, 2026
1f96d31
Merge branch 'main' into codex/public-release
techartdev Sep 10, 2026
9873e60
Cancel attachment preparation before native startup
techartdev Sep 10, 2026
3c36066
Format newly merged Rust changes
techartdev Sep 10, 2026
e49a94f
Install Tauri dependencies in Linux CI
techartdev Sep 10, 2026
68b3e34
Box queued transfer jobs
techartdev Sep 10, 2026
ca88e24
Merge branch 'main' into codex/public-release
techartdev Sep 10, 2026
3c9267a
Reduce duplicate CI work
techartdev Sep 10, 2026
1d2942b
Fix Rust 1.98 Clippy compatibility [skip ci]
techartdev Sep 10, 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
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
verify:
runs-on: windows-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run public:check
- run: npm run verify

rust-sdks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- name: Test publishable Rust SDKs
run: cargo test -p shellcanvas-filesystem-sdk -p shellcanvas-adapter-sdk --locked
- name: Lint publishable Rust SDKs
run: cargo clippy -p shellcanvas-filesystem-sdk -p shellcanvas-adapter-sdk --all-targets --locked -- -D warnings
45 changes: 45 additions & 0 deletions .github/workflows/publish-sdks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Publish SDKs

on:
workflow_dispatch:
inputs:
version:
description: Existing vX.Y.Z tag to publish
required: true
type: string

permissions:
contents: read
id-token: write

jobs:
npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ inputs.version }}
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm run release:check -- "${{ inputs.version }}"
- run: npm test --workspace @shellcanvas/app-sdk
- run: npm publish --workspace @shellcanvas/app-sdk --access public

crates:
runs-on: ubuntu-latest
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ inputs.version }}
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
- run: npm run release:check -- "${{ inputs.version }}"
- run: cargo test -p shellcanvas-filesystem-sdk -p shellcanvas-adapter-sdk --locked
- run: cargo publish -p shellcanvas-filesystem-sdk --locked
- run: cargo publish -p shellcanvas-adapter-sdk --locked
42 changes: 42 additions & 0 deletions .github/workflows/release-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Release Windows installers

on:
push:
tags: ["v*"]

permissions:
contents: write

jobs:
installers:
runs-on: windows-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run release:check -- "${{ github.ref_name }}"
- run: npm run public:check
- run: npm run verify
- run: npm run release:windows
- name: Generate SHA-256 checksums
shell: pwsh
run: |
$files = Get-ChildItem target/release/bundle/nsis/*-setup.exe,target/release/bundle/msi/*.msi
$lines = $files | ForEach-Object { "{0} {1}" -f (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(), $_.Name }
$lines | Set-Content target/release/bundle/SHA256SUMS.txt -Encoding ascii
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
shell: pwsh
run: |
gh release create "${{ github.ref_name }}" `
target/release/bundle/nsis/*-setup.exe `
target/release/bundle/msi/*.msi `
target/release/bundle/SHA256SUMS.txt `
--repo "${{ github.repository }}" `
--title "ShellCanvas ${{ github.ref_name }}" `
--generate-notes `
--verify-tag
8 changes: 4 additions & 4 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ Scope is flexibility now, not implementing every protocol now. Pick the first re
- [ ] **SHIP-03 — Tablet feasibility.** Tauri Android/iOS spikes covering SSH lifecycle, key import, terminal IME, external keyboard and touch window management. Gate: actual device tests; browser responsiveness is insufficient. Phone refinement follows tablet proof.
- [ ] **SHIP-04 — Web gateway design.** Authenticated gateway, per-user host access, credential policy and private-network routing. Gate: threat model and deployment prototype before offering hosted access. Commercial terms remain open.
- [x] **COMM-00 — Product name.** User selected ShellCanvas; app title, package/crate names, application identity and documentation updated. Workspace folder stays in its existing location.
- [ ] **COMM-01 — Community launch.** Prepare contribution templates, supported-system matrix, SDK examples and release notes. Domain/trademark checks and public release remain separate from the private repository. No paid-tier commitment yet.
- [ ] **COMM-01 — Community launch.** Maintain contribution templates, the supported-system matrix, SDK examples and release notes. Domain/trademark checks and paid-tier planning remain separate work.

## Validation record

- Permission slice: the actual SFTP adapter running as the existing nobody account on evtinsait denied private reads/downloads and forbidden create/save/rename/delete/upload, preserving original files without temporary leftovers. The same services then completed allowed editing and binary transfers. Exact disposable-file/directory cleanup and disconnect passed. Browser folder/editor refusal retained input/drafts and a later retry succeeded; all-target Clippy passed. See [permission validation](docs/permission-validation.md).
- Permission slice: the actual SFTP adapter running as an unprivileged account on an authorized Linux test host denied private reads/downloads and forbidden create/save/rename/delete/upload, preserving original files without temporary leftovers. The same services then completed allowed editing and binary transfers. Exact disposable-file/directory cleanup and disconnect passed. Browser folder/editor refusal retained input/drafts and a later retry succeeded; all-target Clippy passed. See [permission validation](docs/permission-validation.md).

- Terminal service slice: 32 Rust tests and Clippy passed, including an all-targets check of the live probe. Neutral runtime fixtures verify blocked-write cancellation, concurrent output, fixed-size consoles, byte preservation and failures. The live two-console evtinsait probe through the SSH service and native pump/registry passed independent state, different dimensions, one-console close, surviving-console input, stale/cross-session refusal and disconnect. No remote files were changed.
- Terminal service slice: 32 Rust tests and Clippy passed, including an all-targets check of the live probe. Neutral runtime fixtures verify blocked-write cancellation, concurrent output, fixed-size consoles, byte preservation and failures. A live two-console probe on an authorized Linux test host passed independent state, different dimensions, one-console close, surviving-console input, stale/cross-session refusal and disconnect. No remote files were changed.

- Provider-selection completion: 29 Rust tests and Clippy passed. Cached probes share concurrent success/failure results, enforce entry/command/output bounds and do not survive an inspection attempt. Ordered failure fallback, unknown-system identity/capability preservation and deadline tests passed. The read-only evtinsait regression passed known-host authentication, Linux inspection, SFTP locations/preview, PTY input/resize and disconnect.
- Provider-selection completion: 29 Rust tests and Clippy passed. Cached probes share concurrent success/failure results, enforce entry/command/output bounds and do not survive an inspection attempt. Ordered failure fallback, unknown-system identity/capability preservation and deadline tests passed. The read-only authorized-host regression passed known-host authentication, Linux inspection, SFTP locations/preview, PTY input/resize and disconnect.

- Windows clipboard/quit walkthrough: Unicode and multiline text copied and pasted between independent editor drafts. Canceling native app quit preserved both; confirmed discard closed the process. Dusk persisted across a full restart, then the original Fjord setting was restored. No remote writes were made. Editor titlebars and dock menus now share stable instance numbering, including document titles; browser labels were checked with two editor windows.

Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

ShellCanvas follows semantic versioning for published SDK packages. Desktop releases use the same version where practical. The project is pre-1.0, so minor releases may include breaking changes with migration notes.

## 0.1.0

Initial public preview:

- Tauri desktop with SSH/SFTP workspaces, Files, Terminal, Editor and Host details.
- Saved hosts, host-key review, reconnect and multi-workspace behavior.
- Runtime app packages, themes and provisional public app APIs.
- Rust adapter and filesystem SDKs for independently packaged connections.
- Windows NSIS and MSI installer build path.

See the repository documentation and release notes for current platform validation and known limitations.
9 changes: 9 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Code of conduct

ShellCanvas contributors and maintainers are expected to keep project spaces respectful, constructive and safe.

Be considerate, discuss ideas and code rather than people, accept correction, and make room for different levels of experience. Harassment, threats, discrimination, sexualized attention, deliberate disruption and publishing another person's private information are unacceptable.

Report conduct concerns privately to the repository maintainers through GitHub. Maintainers may edit or remove contributions, comments and other participation that violates these expectations, and may temporarily or permanently restrict participation. Reports will be handled as privately as practical.

This policy applies in project repositories, issue trackers, review discussions and public spaces where someone represents ShellCanvas.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

ShellCanvas is at the prototype stage. Small, focused contributions and compatibility reports will be most useful while the interfaces settle.

Use [the roadmap](ROADMAP.md) for priorities and [the backlog](BACKLOG.md) for task IDs and completion gates. Keep the backlog current with implementation and validation; do not mark an entire platform supported from a fixture or browser preview alone.
Use [the documentation map](docs/README.md), [roadmap](ROADMAP.md) and [backlog](BACKLOG.md) for priorities and completion gates. Keep the backlog current with implementation and validation; do not mark an entire platform supported from a fixture or browser preview alone. Security reports follow [the private reporting policy](SECURITY.md), not the public issue tracker.

## Development

Expand Down
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["crates/service-contracts", "crates/adapter-sdk", "crates/adapter-runtime", "crates/ssh-core", "src-tauri"]
members = ["crates/filesystem-sdk", "crates/service-contracts", "crates/adapter-sdk", "crates/adapter-runtime", "crates/ssh-core", "src-tauri"]
resolver = "2"

# Released Tauri still requires Tao 0.35. Pin the upstream Windows keyboard
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ A desktop canvas for remote devices. Built with **Tauri 2, Rust, and TypeScript/

This is an early working prototype, not a complete file manager or a hardened public release.

[User documentation](https://shellcanvas.com/docs/) · [Engineering documentation](docs/README.md) · [Security policy](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)

## Downloads

Windows NSIS and MSI installers are attached to versioned [GitHub Releases](https://github.com/techartdev/ShellCanvas/releases). The public preview is unsigned, so Windows may show an unknown-publisher warning. Check the release's `SHA256SUMS.txt` before installing. Other native platforms remain development targets with the validation limits described below.

The architecture is evolving toward a connection-neutral desktop: SSH is the first/default adapter. [Installed native adapters](docs/adapter-packages.md) can provide files, consoles, optional text/file changes and remote settings through independently assigned connections in one workspace, alongside **SSH (built in)**. These assignments can be stored in [saved workspace profiles](docs/workspace-profiles.md), with credentials omitted and revision-checked updates. See [the composition design](docs/connections.md). Production Serial, Telnet, FTP and device API adapters are not implemented yet.

## Run
Expand Down Expand Up @@ -97,7 +103,7 @@ Canvas variants, local wallpapers, interface scaling and toolbar/dock sizing.
Theme authors can start from [Canvas Study](examples/themes/canvas-study) without
an SDK or build step.

Start with [the roadmap](ROADMAP.md), [development backlog](BACKLOG.md), and [kernel/API roadmap](docs/kernel-roadmap.md). Build independent apps with the [standalone app SDK and starter](docs/app-sdk.md). See [the architecture](docs/architecture.md), [bundled app guide](docs/apps.md), [runtime app guide](docs/runtime-apps.md), [remote support matrix](docs/providers.md), [WispCrew AI integration assessment](docs/ai-integration.md), and [contribution guide](CONTRIBUTING.md). The public SDK remains provisional and is distributed as a local tarball; it has not been published to npm. Commercial packaging is intentionally undecided.
Start with [the documentation map](docs/README.md), [roadmap](ROADMAP.md), [development backlog](BACKLOG.md), and [kernel/API roadmap](docs/kernel-roadmap.md). Build independent apps with the [standalone app SDK and starter](docs/app-sdk.md). See [the architecture](docs/architecture.md), [bundled app guide](docs/apps.md), [runtime app guide](docs/runtime-apps.md), [remote support matrix](docs/providers.md), [WispCrew AI integration assessment](docs/ai-integration.md), and [contribution guide](CONTRIBUTING.md). The public SDK remains provisional while the interfaces settle. Commercial packaging is intentionally undecided.

Device integrations can use the [standalone Rust adapter SDK and CLI](docs/adapter-sdk.md).
The repository's [AI development skills](docs/ai-development-skills.md) guide
Expand Down
21 changes: 21 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Security policy

ShellCanvas handles remote credentials, host identity, files, terminals and third-party extension packages. Please report suspected vulnerabilities privately instead of opening a public issue.

Use GitHub's **Report a vulnerability** form in the repository Security tab. Include the affected version, platform, prerequisites, impact and a minimal reproduction. Remove passwords, private keys, tokens, private host names and user file contents from reports.

The maintainers will acknowledge a report, investigate it and coordinate a fix and disclosure when the issue is confirmed. There is no paid bug-bounty program unless a separate program says otherwise.

## Supported versions

ShellCanvas is an early prototype. Security fixes are applied to the latest release and the current default branch. Older builds are not supported.

## Important boundaries

- The terminal has the permissions of the authenticated remote account.
- Host keys are checked before authentication; changed and revoked keys remain blocked.
- Installed native adapters execute with the local user's operating-system permissions and require explicit trust.
- Installed runtime-app isolation currently has targeted Windows/WebView2 validation. Other platforms and hostile-code containment are not claimed.
- Browser fixtures and development previews use synthetic data and do not establish native production security.

These boundaries describe the current design; they do not replace coordinated vulnerability reporting.
1 change: 1 addition & 0 deletions crates/adapter-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod custom;
mod diagnostics;
mod process;
mod process_tree;
pub use process_tree::ProcessTree;
mod services;
mod standard;
mod transfers;
Expand Down
3 changes: 2 additions & 1 deletion crates/adapter-runtime/src/process_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
use std::io;
use tokio::process::{Child, Command};

pub(crate) struct ProcessTree {
/// Ownership of a trusted native helper. This is lifecycle control, not a sandbox.
pub struct ProcessTree {
#[cfg(windows)]
job: windows::Job,
}
Expand Down
6 changes: 6 additions & 0 deletions crates/adapter-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ edition = "2021"
license = "MPL-2.0"
description = "Versioned process protocol and concurrent server for ShellCanvas connection adapters"
readme = "README.md"
repository = "https://github.com/techartdev/ShellCanvas"
homepage = "https://shellcanvas.com/docs/adapter-sdk/quickstart.html"
documentation = "https://docs.rs/shellcanvas-adapter-sdk"
keywords = ["shellcanvas", "adapter", "remote", "device", "protocol"]
categories = ["api-bindings", "asynchronous"]
rust-version = "1.93"

[dependencies]
anyhow = "1"
Expand Down
50 changes: 50 additions & 0 deletions crates/adapter-sdk/src/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashSet;
const MAX_PACKAGE_FILES: usize = 4096;
const MAX_PACKAGE_BYTES: u64 = 512 * 1024 * 1024;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PackageFile {
Expand Down Expand Up @@ -99,6 +101,9 @@ impl Manifest {
if self.arguments.iter().any(|arg| arg.contains('\0')) {
bail!("Invalid adapter launch argument");
}
if self.files.is_empty() || self.files.len() > MAX_PACKAGE_FILES {
bail!("Adapter packages must contain 1 to 4096 files");
}
let mut paths = HashSet::new();
let mut size = 0u64;
for entry in &self.files {
Expand All @@ -115,6 +120,9 @@ impl Manifest {
size = size
.checked_add(entry.size)
.context("Adapter package size overflow")?;
if size > MAX_PACKAGE_BYTES {
bail!("Adapter package exceeds the 512 MiB payload budget");
}
}
if !self
.files
Expand Down Expand Up @@ -172,6 +180,7 @@ impl Manifest {
Ok(Value::Object(output))
}
}

impl ConfigField {
fn accepts(&self, value: &Value) -> bool {
match self.kind {
Expand All @@ -181,3 +190,44 @@ impl ConfigField {
}
}
}

#[cfg(test)]
mod package_limits {
use super::*;

fn manifest(files: Vec<PackageFile>) -> Manifest {
Manifest {
schema_version: 1,
id: "org.example.adapter".into(),
name: "Example".into(),
version: "1.0.0".into(),
description: String::new(),
platform: "windows-x86_64".into(),
entrypoint: "adapter.exe".into(),
arguments: vec![],
files,
configuration: vec![],
}
}

fn file(size: u64) -> PackageFile {
PackageFile {
path: "adapter.exe".into(),
size,
sha256: "a".repeat(64),
executable: true,
}
}

#[test]
fn bounds_review_payload_before_staging() {
assert!(manifest(vec![]).validate().is_err());
assert!(manifest(vec![file(MAX_PACKAGE_BYTES)]).validate().is_ok());
assert!(manifest(vec![file(MAX_PACKAGE_BYTES + 1)])
.validate()
.is_err());
assert!(manifest((0..=MAX_PACKAGE_FILES).map(|_| file(0)).collect())
.validate()
.is_err());
}
}
Loading