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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ CORS_ORIGINS=http://localhost:3000,http://localhost:3001,https://app.nvisy.com
CORS_MAX_AGE=1h
CORS_ALLOW_CREDENTIALS=true

# Client IP source: where the caller's IP is read from for security/audit
# records. Defaults to `ConnectInfo` (the TCP peer), which cannot be spoofed and
# is correct for a directly exposed server. Behind a proxy that sets a
# forwarding header, set the matching source or the recorded IP is the proxy's:
# RightmostXForwardedFor — proxy appends to X-Forwarded-For (e.g. Render)
# XRealIp | CfConnectingIp | TrueClientIp | FlyClientIp — provider-specific
CLIENT_IP_SOURCE=ConnectInfo

# OpenAPI
OPENAPI_JSON_PATH=/api/openapi.json
OPENAPI_SCALAR_PATH=/api/scalar
Expand Down
78 changes: 27 additions & 51 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
Expand Up @@ -167,7 +167,7 @@ csv = { version = "1.4", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = [] }
toml = { version = "1.1", features = [] }
validator = { version = "0.21", features = ["derive"] }
garde = { version = "0.23", features = ["derive", "email", "url"] }

# Text processing
slug = { version = "0.1", features = [] }
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ credential encryption.

## Features

- **Multimodal redaction** detect and remove sensitive data across PDFs, office documents, images, and audio.
- **AI-powered detection** LLM- and pattern-driven PII/entity recognition, governed by configurable redaction policies.
- **Reviewer edits**suppress a false positive, retag a detection, or add one the analysis missed, then re-redact as many times as needed.
- **Workspace isolation** multi-tenant workspaces with HKDF-derived, per-workspace credential encryption.
- **Real-time collaboration** WebSocket and NATS pub/sub for live status and document editing.
- **Interactive docs** auto-generated OpenAPI served through a Scalar UI.
- **Multimodal redaction**: detect and remove sensitive data across PDFs, office documents, images, and audio.
- **AI-powered detection**: LLM- and pattern-driven PII/entity recognition, governed by configurable redaction policies.
- **Reviewer edits**: suppress a false positive, retag a detection, or add one the analysis missed, then re-redact, as many times as needed.
- **Workspace isolation**: multi-tenant workspaces with HKDF-derived, per-workspace credential encryption.
- **Real-time collaboration**: WebSocket and NATS pub/sub for live status and document editing.
- **Interactive docs**: auto-generated OpenAPI served through a Scalar UI.

## Requirements

- **Rust + Cargo** 1.95+, Edition 2024
- **PostgreSQL** 18+, **NATS** 2.10+ (JetStream), and an **S3-compatible blob store** (RustFS by default) — the dev compose file provides all three
- **Rust + Cargo**: 1.95+, Edition 2024
- **PostgreSQL** 18+, **NATS** 2.10+ (JetStream), and an **S3-compatible blob store** (RustFS by default). The dev compose file provides all three.

## Quick start

Expand Down Expand Up @@ -78,10 +78,10 @@ configuration.

See [`docs/`](docs/) for the details:

- [Architecture](docs/ARCHITECTURE.md) the crates, the detect/redact pipeline, and how they fit together.
- [Intelligence](docs/INTELLIGENCE.md) detection capabilities and the redaction engine.
- [Providers](docs/PROVIDERS.md) inference and object-store provider design.
- [Security](docs/SECURITY.md) the encryption, authentication, and isolation model.
- [Architecture](docs/ARCHITECTURE.md): the crates, the detect/redact pipeline, and how they fit together.
- [Intelligence](docs/INTELLIGENCE.md): detection capabilities and the redaction engine.
- [Providers](docs/PROVIDERS.md): inference and object-store provider design.
- [Security](docs/SECURITY.md): the encryption, authentication, and isolation model.

## Contributing

Expand Down
4 changes: 2 additions & 2 deletions crates/nvisy-cli/src/server/http_server.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! HTTP server implementation using enhanced lifecycle management.

use std::io;
use std::net::SocketAddr;
use std::time::Duration;

use axum::Router;
use nvisy_server::extract::AppConnectInfo;
use tokio::net::TcpListener;
use tokio_util::sync::CancellationToken;

Expand Down Expand Up @@ -46,7 +46,7 @@ pub async fn serve_http(
}
};

let app = app.into_make_service_with_connect_info::<AppConnectInfo>();
let app = app.into_make_service_with_connect_info::<SocketAddr>();
let serve = axum::serve(listener, app).with_graceful_shutdown(graceful);

// Bound the graceful drain: if a connection has not closed within the
Expand Down
4 changes: 2 additions & 2 deletions crates/nvisy-cli/src/server/https_server.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! HTTPS server implementation using enhanced lifecycle management.

use std::io;
use std::net::SocketAddr;
use std::path::Path;

use axum::Router;
use axum_server::tls_rustls::RustlsConfig;
use nvisy_server::extract::AppConnectInfo;
use tokio_util::sync::CancellationToken;

use super::TRACING_TARGET_STARTUP;
Expand Down Expand Up @@ -68,7 +68,7 @@ pub async fn serve_https(

axum_server::bind_rustls(server_addr, tls_config)
.handle(handle)
.serve(app.into_make_service_with_connect_info::<AppConnectInfo>())
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
.await
})
.await
Expand Down
4 changes: 2 additions & 2 deletions crates/nvisy-file-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ OAuth-based cloud file-service providers for the Nvisy platform.

## Overview

Connects a workspace to a tenant's consumer file service Google Drive,
Dropbox, OneDrive, and Box behind a single `FileServiceClient`. It owns
Connects a workspace to a tenant's consumer file service (Google Drive,
Dropbox, OneDrive, and Box) behind a single `FileServiceClient`. It owns
the OAuth2 authorization-code flow (with refresh), driven through a shared
`reqwest` client, and the per-provider REST calls to list and stream files
in and out. Sibling to `nvisy-object-store`, which covers object stores
Expand Down
29 changes: 29 additions & 0 deletions crates/nvisy-postgres/src/model/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,35 @@ pub struct Account {
pub deleted_at: Option<Timestamp>,
}

impl Account {
/// An account row with a fresh unique handle and matching email, for tests.
/// Not admin/verified/suspended; timestamps are now. Useful to downstream
/// crates that need an `Account` without a database.
#[cfg(any(feature = "test_util", test))]
#[must_use]
pub fn test() -> Self {
let username = Handle::test();
let email_address = format!("{}@example.com", username.as_str());
let now: Timestamp = jiff::Timestamp::now().into();
Self {
id: Uuid::now_v7(),
is_admin: false,
is_verified: false,
is_suspended: false,
username,
display_name: None,
email_address,
avatar_url: None,
timezone: "UTC".to_owned(),
locale: "en".to_owned(),
password_changed_at: None,
created_at: now,
updated_at: now,
deleted_at: None,
}
}
}

/// Data for creating a new account.
#[derive(Debug, Clone, Insertable)]
#[diesel(table_name = accounts)]
Expand Down
23 changes: 23 additions & 0 deletions crates/nvisy-postgres/src/model/account_api_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ pub struct AccountApiToken {
pub deleted_at: Option<Timestamp>,
}

impl AccountApiToken {
/// A token row of `session_type` for `account_id`, for tests. `issued_at` is
/// now and there is no expiry; the remaining fields are defaulted. Useful to
/// downstream crates building auth flows without a database.
#[cfg(any(feature = "test_util", test))]
#[must_use]
pub fn test(account_id: Uuid, session_type: ApiTokenType) -> Self {
Self {
id: Uuid::now_v7(),
account_id,
display_name: "Test Token".to_owned(),
session_type,
ip_address: None,
user_agent: None,
is_remembered: false,
issued_at: jiff::Timestamp::now().into(),
expired_at: None,
last_used_at: None,
deleted_at: None,
}
}
}

/// Data for creating a new account API token.
#[derive(Debug, Default, Clone, Insertable)]
#[diesel(table_name = account_api_tokens)]
Expand Down
4 changes: 2 additions & 2 deletions crates/nvisy-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ serde_json = { workspace = true, features = [] }
toml = { workspace = true, features = ["parse"] }
bytes = { workspace = true, features = [] }
http-body = { workspace = true, features = [] }
validator = { workspace = true, features = [] }
garde = { workspace = true, features = [] }

# Text processing
woothee = { workspace = true, features = [] }
Expand Down Expand Up @@ -142,7 +142,7 @@ url = { workspace = true, features = [] }
[dev-dependencies]
# Internal crates
nvisy-nats = { workspace = true, features = [] }
nvisy-postgres = { workspace = true, features = ["schema"] }
nvisy-postgres = { workspace = true, features = ["schema", "test_util"] }
nvisy-webhook = { workspace = true, features = ["reqwest"] }

axum-test = { workspace = true, features = [] }
Expand Down
2 changes: 1 addition & 1 deletion crates/nvisy-server/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use nvisy_postgres::PgConfig;
use nvisy_webhook::WebhookService;

use crate::Result;
use crate::handler::CookieConfig;
use crate::middleware::UploadConfig;
use crate::response::CookieConfig;
use crate::service::{
CryptoConfig, EngineConfig, FileConnectorsConfig, HealthConfig, IntegrationConfig, OidcConfig,
S3Config, ServiceState, SessionKeysConfig,
Expand Down
Loading