Skip to content
Open
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
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,19 @@ env:

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

- name: Free disk space (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc
sudo apt-get clean

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

Expand All @@ -38,11 +47,8 @@ jobs:
- name: Cache cargo
uses: Swatinem/rust-cache@v2

- name: Build
run: cargo build --verbose

- name: Run tests
run: cargo test --verbose
run: cargo test

lint:
runs-on: ubuntu-latest
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
name: sediment-x86_64-unknown-linux-musl
- target: x86_64-pc-windows-msvc
os: windows-latest
name: sediment-x86_64-pc-windows-msvc

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -58,7 +61,8 @@ jobs:
if: ${{ !contains(matrix.target, 'musl') }}
run: cargo build --release --target ${{ matrix.target }}

- name: Package
- name: Package (Unix)
if: ${{ !contains(matrix.target, 'windows') }}
run: |
mkdir -p dist
cp target/${{ matrix.target }}/release/sediment dist/
Expand All @@ -67,13 +71,25 @@ jobs:
cd ..
shasum -a 256 ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256

- name: Package (Windows)
if: contains(matrix.target, 'windows')
shell: pwsh
run: |
mkdir dist
cp target/${{ matrix.target }}/release/sediment.exe dist/
Compress-Archive -Path dist/sediment.exe -DestinationPath ${{ matrix.name }}.zip
$hash = (Get-FileHash ${{ matrix.name }}.zip -Algorithm SHA256).Hash.ToLower()
"$hash ${{ matrix.name }}.zip" | Out-File -Encoding ascii ${{ matrix.name }}.zip.sha256

- name: Upload Release Assets
uses: softprops/action-gh-release@v1
with:
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
files: |
${{ matrix.name }}.tar.gz
${{ matrix.name }}.tar.gz.sha256
${{ matrix.name }}.zip
${{ matrix.name }}.zip.sha256

checksums:
needs: build
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Combines vector search, a relationship graph, and access tracking into a unified
- **Single binary, zero config** — no Docker, no Postgres, no Qdrant. Just `sediment`.
- **50ms store, 103ms recall** — local embeddings and vector search at 1K items, no network round-trips.
- **4-tool focused API** — `store`, `recall`, `list`, `forget`. That's it.
- **Works everywhere** — macOS (Intel + ARM), Linux x86_64. All data stays on your machine.
- **Works everywhere** — macOS (Intel + ARM), Linux x86_64, Windows x86_64. All data stays on your machine.

### Comparison

Expand All @@ -36,9 +36,12 @@ cargo install sediment-mcp
brew tap rendro/tap
brew install sediment

# Via shell installer
# Via shell installer (macOS/Linux)
curl -fsSL https://raw.githubusercontent.com/rendro/sediment/main/install.sh | sh

# Via PowerShell installer (Windows)
irm https://raw.githubusercontent.com/rendro/sediment/main/install.ps1 | iex

# From source
cargo install --path .
```
Expand Down Expand Up @@ -195,9 +198,14 @@ Benchmarked against 5 alternatives with 1,000 memories and 200 queries. See [BEN

### Data Location

**macOS / Linux:**
- Vector store: `~/.sediment/data/`
- Graph + access tracking: `~/.sediment/access.db`

**Windows:**
- Vector store: `%LOCALAPPDATA%\sediment\data\`
- Graph + access tracking: `%LOCALAPPDATA%\sediment\access.db`

Everything runs locally. Your data never leaves your machine.

## Contributing
Expand Down
84 changes: 84 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Install sediment on Windows.
.DESCRIPTION
Downloads the latest sediment release from GitHub and installs it to
$env:LOCALAPPDATA\sediment\bin (or $env:SEDIMENT_INSTALL_DIR if set).
#>
[CmdletBinding()]
param()

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$Repo = 'rendro/sediment'
$InstallDir = if ($env:SEDIMENT_INSTALL_DIR) { $env:SEDIMENT_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'sediment\bin' }

# Detect architecture
$Arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
if ($Arch -ne 'X64') {
Write-Error "Unsupported architecture: $Arch. Only x86_64 is supported."
exit 1
}

$Target = 'x86_64-pc-windows-msvc'

# Get latest version
Write-Host 'Fetching latest release...'
$Release = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest"
$Version = $Release.tag_name -replace '^v', ''
if (-not $Version) {
Write-Error 'Failed to determine latest version'
exit 1
}
Write-Host "Installing sediment v$Version ($Target)..."

$ZipName = "sediment-$Target.zip"
$Url = "https://github.com/$Repo/releases/download/v$Version/$ZipName"

# Download
$TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "sediment-install-$([guid]::NewGuid())"
New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null

try {
$ZipPath = Join-Path $TmpDir $ZipName
Invoke-WebRequest -Uri $Url -OutFile $ZipPath -UseBasicParsing

# Verify checksum
$ChecksumsUrl = "https://github.com/$Repo/releases/download/v$Version/checksums.txt"
try {
$ChecksumsPath = Join-Path $TmpDir 'checksums.txt'
Invoke-WebRequest -Uri $ChecksumsUrl -OutFile $ChecksumsPath -UseBasicParsing
$Expected = (Get-Content $ChecksumsPath | Where-Object { $_ -match $ZipName } | ForEach-Object { ($_ -split '\s+')[0] })
if ($Expected) {
$Actual = (Get-FileHash $ZipPath -Algorithm SHA256).Hash.ToLower()
if ($Actual -ne $Expected) {
Write-Error "Checksum verification failed!`n Expected: $Expected`n Actual: $Actual"
exit 1
}
Write-Host 'Checksum verified.'
}
} catch {
Write-Warning 'No checksums.txt available, binary integrity could not be verified.'
}

# Extract
$ExtractDir = Join-Path $TmpDir 'extract'
Expand-Archive -Path $ZipPath -DestinationPath $ExtractDir -Force

# Install
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
Copy-Item -Path (Join-Path $ExtractDir 'sediment.exe') -Destination (Join-Path $InstallDir 'sediment.exe') -Force

Write-Host "Installed sediment to $InstallDir\sediment.exe"

# Check PATH
if (-not ($env:PATH -split ';' | Where-Object { $_ -eq $InstallDir })) {
Write-Host ''
Write-Host "Add $InstallDir to your PATH:"
Write-Host " [Environment]::SetEnvironmentVariable('PATH', `"$InstallDir;`$env:PATH`", 'User')"
}
} finally {
Remove-Item -Path $TmpDir -Recurse -Force -ErrorAction SilentlyContinue
}
27 changes: 22 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,29 @@ impl std::fmt::Display for ListScope {

/// Get the central database path.
///
/// Returns `~/.sediment/data` or the path specified in `SEDIMENT_DB` environment variable.
/// Returns `~/.sediment/data` on Unix, `%LOCALAPPDATA%\sediment\data` on Windows,
/// or the path specified in `SEDIMENT_DB` environment variable.
/// Note: LanceDB uses a directory, not a single file.
pub fn central_db_path() -> PathBuf {
if let Ok(path) = std::env::var("SEDIMENT_DB") {
return PathBuf::from(path);
}

dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".sediment")
.join("data")
#[cfg(unix)]
{
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".sediment")
.join("data")
}

#[cfg(windows)]
{
dirs::data_local_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("sediment")
.join("data")
}
}

/// Project configuration stored in `.sediment/config`
Expand Down Expand Up @@ -266,6 +278,11 @@ fn write_config_atomic(
let tmp_path = sediment_dir.join(format!("config.tmp.{}", std::process::id()));
std::fs::write(&tmp_path, &content)?;

// On Windows, rename fails if destination exists — remove it first.
// This is not atomic but acceptable for a config file.
#[cfg(windows)]
let _ = std::fs::remove_file(config_path);

if let Err(e) = std::fs::rename(&tmp_path, config_path) {
let _ = std::fs::remove_file(&tmp_path);
return Err(e);
Expand Down