
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
just-bash-postgres
Advanced tools
PostgreSQL filesystem provider for just-bash with ltree, FTS, vector search, and RLS
A PostgreSQL-backed filesystem provider for just-bash. Implements the IFileSystem interface using a single fs_nodes table with ltree for hierarchy, built-in full-text search, optional pgvector semantic search, and row-level security for per-session isolation.
IFileSystem implementation -- files, directories, symlinks, hard links, chmod, stat, recursive cp/rm/mvtsvector with weighted filename + content rankingsessionId, enforced by both application logic and RLSsetup() on every startupbun add just-bash-postgres
Or with npm:
npm install just-bash-postgres
import postgres from "postgres";
import { PgFileSystem } from "just-bash-postgres";
import { Bash } from "just-bash";
const sql = postgres("postgres://user:pass@localhost:5432/mydb");
const fs = new PgFileSystem({ sql, sessionId: 1 });
await fs.setup(); // creates tables, indexes, and RLS policies
const bash = new Bash({ fs, cwd: "/", defenseInDepth: false });
await bash.exec('echo "hello" > /greeting.txt');
const result = await bash.exec("cat /greeting.txt");
console.log(result.stdout); // "hello\n"
Note:
defenseInDepth: falseis required when using just-bash with postgres.js because the defense-in-depth sandbox restricts raw network access that postgres.js needs for its connection.
fs.setup() runs an idempotent migration that creates the fs_nodes table, indexes, and RLS policies, along with the root directory for the session. Safe to call on every startup -- all statements use IF NOT EXISTS guards.
If you pass embeddingDimensions in the options, setup() also creates the pgvector extension and adds an embedding column with an HNSW index.
Three search methods are available beyond the standard IFileSystem interface.
Always available. Uses PostgreSQL's tsvector with filename weighted higher than content.
const results = await fs.search("database migration");
// [{ path: "/docs/migration-guide.txt", name: "migration-guide.txt", rank: 0.8, snippet: "..." }]
Requires an embedding provider. Uses pgvector cosine similarity over HNSW indexes.
const fs = new PgFileSystem({
sql,
sessionId: 1,
embed: async (text) =>
openai.embeddings
.create({ input: text, model: "text-embedding-3-small" })
.then((r) => r.data[0].embedding),
embeddingDimensions: 1536,
});
await fs.setup();
const results = await fs.semanticSearch("how to deploy the app");
Combines full-text and vector search with configurable weights (default: 0.4 text, 0.6 vector).
const results = await fs.hybridSearch("deployment guide", {
textWeight: 0.3,
vectorWeight: 0.7,
limit: 10,
});
All search methods accept an optional path parameter to scope results to a subtree:
const results = await fs.search("config", { path: "/app/settings" });
interface SearchResult {
path: string;
name: string;
rank: number;
snippet?: string; // only present for full-text search
}
Each PgFileSystem instance is bound to a sessionId. All queries include WHERE session_id = $sessionId, and the database schema enforces the same constraint via RLS policies. Sessions cannot see or modify each other's files.
const sessionAFs = new PgFileSystem({ sql, sessionId: 1 });
const sessionBFs = new PgFileSystem({ sql, sessionId: 2 });
await sessionAFs.setup();
await sessionBFs.setup();
await sessionAFs.writeFile("/secret.txt", "session A data");
await sessionBFs.exists("/secret.txt"); // false -- completely isolated
No sessions table is required. sessionId is just a positive integer; session management is the consuming application's responsibility.
interface PgFileSystemOptions {
/** postgres.js connection instance */
sql: postgres.Sql;
/** Positive integer session ID for isolation. All operations are scoped to this session. */
sessionId: number;
/** Maximum file size in bytes (default: 100MB) */
maxFileSize?: number;
/** Statement timeout in milliseconds (default: 30000) */
statementTimeout?: number;
/** Async function that returns an embedding vector for text content.
When provided, writeFile generates embeddings automatically. */
embed?: (text: string) => Promise<number[]>;
/** Dimension of embedding vectors. Required if embed is provided.
Must match your embed function output (e.g. 1536 for text-embedding-3-small). */
embeddingDimensions?: number;
}
| Method | Description |
|---|---|
setup() | Create schema, indexes, RLS policies, and root directory |
writeFile(path, content) | Create or overwrite a file |
readFile(path) | Read file as UTF-8 string |
readFileBuffer(path) | Read file as Uint8Array |
appendFile(path, content) | Append to a file |
exists(path) | Check if path exists |
stat(path) | Get file stats (follows symlinks) |
lstat(path) | Get file stats (does not follow symlinks) |
mkdir(path, options?) | Create directory ({ recursive: true } supported) |
readdir(path) | List directory entries as strings |
readdirWithFileTypes(path) | List directory entries with type info |
rm(path, options?) | Delete file or directory ({ recursive: true } supported) |
mv(src, dest) | Move/rename file or directory |
cp(src, dest, options?) | Copy file or directory ({ recursive: true } supported) |
chmod(path, mode) | Change file mode |
utimes(path, atime, mtime) | Update modification time |
symlink(target, path) | Create a symbolic link |
readlink(path) | Read symlink target |
link(src, dest) | Create a hard link (copies content) |
realpath(path) | Resolve symlinks (max 16 levels) |
| Method | Description |
|---|---|
search(query, options?) | Full-text search with websearch syntax |
semanticSearch(query, options?) | Vector cosine similarity search |
hybridSearch(query, options?) | Combined text + vector search |
| Export | Description |
|---|---|
setupSchema(sql) | Run schema migration standalone |
setupVectorColumn(sql, dimensions) | Add vector column standalone |
FsError | Error class with POSIX codes (ENOENT, EISDIR, etc.) |
The sessionId is trusted without verification. The library assumes the consuming application has validated the session before constructing a PgFileSystem instance.
Use TLS for database connections in production:
const sql = postgres("postgres://user:pass@host:5432/db?sslmode=require");
Isolation is enforced at two levels: application-level WHERE session_id = ... on every query, and database-level RLS policies. For RLS to be effective, connect as a non-superuser role -- PostgreSQL superusers bypass RLS.
setup() automatically grants permissions to a role named fs_app if it exists:
CREATE ROLE fs_app LOGIN PASSWORD 'your-password';
GRANT CONNECT ON DATABASE your_db TO fs_app;
Run setup() once with a superuser connection to create the schema, then use fs_app for normal operations:
const sql = postgres("postgres://fs_app:your-password@localhost:5432/mydb");
Setting defenseInDepth: false on the just-bash Bash instance disables just-bash's built-in sandbox, which is necessary because postgres.js requires raw network access. Compensate with network-level controls (firewall rules, VPC configuration) to restrict what the host can reach.
git clone https://github.com/F1nnM/just-bash-postgres.git
cd just-bash-postgres
bun install
docker compose up -d
Tests run against a real PostgreSQL instance (110+ tests across 7 files):
docker compose up -d
bun test
By default, tests connect to postgres://postgres@localhost:5433/just_bash_postgres_test. Override with TEST_DATABASE_URL.
bun run typecheck
Releases are published to npm automatically via GitHub Actions when you create a release on GitHub.
To publish manually:
npm publish
Requires
NPM_TOKENsecret in the repository settings for automated publishing.
FAQs
PostgreSQL filesystem provider for just-bash with ltree, FTS, vector search, and RLS
We found that just-bash-postgres demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.