今日已更新 88 条资讯 | 累计 40862 条内容
关于我们

标签:#back

找到 280 篇相关文章

AI 资讯

Defeating the Multi-Tenant SaaS Concurrency Trap in PostgreSQL

Most backend engineers implement multi-tenant quota checks using a standard "read-then-write" pattern. In production, this pattern is highly unsafe: SELECT grading_scans_remaining FROM profiles; If greater than 0, execute the application logic. UPDATE profiles SET grading_scans_remaining = grading_scans_remaining - 1; Under high volume or rapid concurrent requests, two independent processes will read the exact same balance before either one deducts usage. This race condition allows multi-tenant users to bypass your billing gates entirely. To solve this, you have to bypass the frontend and application-level checks, enforcing an atomic database operation that serializes the row update first. I have open-sourced a reference framework that outlines explicit subscription enums, core multi-tenant schemas, and a native VS Code / Cursor snippets configuration to speed up your local database modeling. 📂 Check out the repository on GitHub: { https://github.com/dollykm49/PostgreSQL-SaaS-Multi-Tenant-Subscription-Architecture-reference-framework- } What's inside the repository: Strictly Typed Enums: Centralized business rules handled natively by the database engine. Granular Balance Tracking: Optimized data-layer mapping for profiles and reset states. postgres-saas.code-snippets Engine: A local IDE configuration file that lets you deploy this core schema straight from your code editor by typing pg- shortcuts. For teams building commercial applications looking to skip weeks of writing custom migrations, testing concurrency edge-cases, and debugging row-locking security rules, the repository also includes a link to the extended 28-page production system bundle. Feedback on the multi-tier validation parameters is highly welcome!

2026-07-25 原文 →
AI 资讯

You Might Not Need Kafka: Building a Job Queue with PostgreSQL

It's easy to reach for the popular tool before asking what your system actually needs. For job queueing, the usual advice is to use RabbitMQ or Kafka. The underlying burden of using these tools will be additional processes to deploy, monitor and reason about. But what if using your existing database is possible? I built a job queueing system with a PostgreSQL database that cleared the bar without adding infrastructure. The next question will be, does this solution meet the criteria of a job queue? A job needs a few things to execute properly within a system. It needs to be persistent, surviving unforeseen crashes. Jobs must not be processed more than once by workers; one job should be processed once by one worker. Also, a job's state has to be tracked through every step. A job state must show when it's pending, completed or failed. That's the bar any solution must clear. With the Postgres approach, persistence comes free. Jobs live in a table so when a worker dies mid-job, the job still exists in a row in the db. Whereas with in-memory queues, a crash loses everything still in memory. A broker like RabbitMQ has to be configured for persistence and if configured wrongly, jobs get lost. A database however is fundamentally built for durability. Now, let's say three workers poll the queue at the same instant and run the same query. They'll see the same pending job at the top and nothing stops them all from grabbing it. If that job is a payment, the customer gets charged three times for one service. All the workers successfully process the job with no indication of an error or alerts. This is the requirement that seems to demand a real message broker, and it's exactly where people assume a database can't compete. It can. Postgres has a specific tool for exactly this. The SQL clause FOR UPDATE is used to lock rows. This can be called on a job when a worker picks it up to process. By default other workers will get blocked during this process, they'll wait for the lock to r

2026-07-25 原文 →
AI 资讯

# From JavaScript to Node.js: Understanding What Really Happens Behind the Scenes (Part 4.3A.1)

# Module Resolution Algorithm (Part 1): How Node.js Finds the Right Module In the previous article, we explored one of the most fascinating parts of Node.js—the hidden Module Wrapper Function. We learned that every CommonJS module is wrapped inside a function before execution, and we also discovered that require() is not a JavaScript feature. It is provided by the Node.js runtime. But a very important mystery still remains. When we write: const fs = require ( " fs " ); or const math = require ( " ./math " ); how does Node.js know where these modules are located? How does it decide whether "fs" is a built-in module or a file inside your project? Why does require("./math") work even if you don't write .js ? And what happens internally before your code starts executing? The answer lies inside one of Node.js's most important systems: The Module Resolution Algorithm Understanding this algorithm is essential because every Node.js application uses it hundreds or even thousands of times while starting. What is Module Resolution? The word resolution simply means: Finding the actual file represented by the string passed to require() . Suppose you write: require ( " ./math " ); To you, "./math" looks like a file. But for Node.js, it is initially nothing more than a string. "./math" Node cannot execute a string. It needs the real file. So its first job is to answer one question: "Which exact file should I load?" The complete process of converting the string inside require() into an actual file on disk is called Module Resolution . Why Does Node Need a Resolution Algorithm? Imagine a project like this: project/ ├── app.js ├── math.js ├── database.js ├── auth.js └── utils/ ├── logger.js └── helper.js Now look at these statements. require ( " ./math " ); require ( " ./database " ); require ( " ./utils/logger " ); require ( " fs " ); require ( " express " ); All of them look similar. But internally they are completely different. Some point to your own files. Some point to Node's bu

2026-07-24 原文 →
AI 资讯

What Redis Is and When to Use It

Redis gets reached for reflexively, "just add Redis," as if it were a single fix for slowness. It's genuinely one of the most useful tools in a backend engineer's kit, but using it well starts with understanding what it actually is: an in-memory data structure store, not just a cache. Once you see it as a fast, versatile store of real data structures, the range of problems it solves cleanly (caching, rate limiting, queues, sessions, leaderboards, locks) stops looking like a grab bag and starts looking like one idea applied many ways. This is the opening article of the Redis Masterclass, and it builds on the PostgreSQL series : Redis usually sits alongside a primary database like Postgres, not instead of it. In-memory is the whole point Redis keeps its data in RAM. That single fact explains most of its character. Reading from memory is orders of magnitude faster than reading from disk, so Redis operations typically complete in well under a millisecond, and a single instance handles a very high request rate. That speed is why it's the default choice for anything on the hot path, where a database round trip would be too slow. The tradeoff is that RAM is smaller and more expensive than disk, and volatile. Redis addresses durability with persistence options we'll cover later, but the mental model to start with is: Redis is fast because it's in memory, and you use it for data that benefits from being fast to access, not as the permanent home for everything. It's a data structure store, not a key-value blob The common misconception is that Redis is a simple key-value store, strings in and strings out. It's much more. Redis stores real data structures as values, each with its own commands: Strings for simple values, counters, and cached blobs. Hashes for objects with fields, like a user record. Lists for ordered sequences and simple queues. Sets for unique collections and membership checks. Sorted sets for ranked data like leaderboards and priority queues. Plus streams, bit

2026-07-24 原文 →
开源项目

BorgShield: Sistema de Backup Linux Eficiente, Fiable y Verificable

Un análisis técnico basado en BorgBackup para entornos Debian/Ubuntu Autor: Arcadio Ortega Reinoso Versión del sistema: 2.1.0 Fecha: Julio 2026 Plataforma objetivo: Debian 11+ / Ubuntu 22.04+ (x86_64) Puedes encontrarlo en: BorgShield Fortalezas Diferenciales Valor Diferencial Claro: La inclusión de 23 tipos de metadatos (repositorios git, dconf, claves GPG, snaps, flatpaks, etc.) resuelve el problema de tener los archivos pero no saber cómo reconstruir el entorno. No es solo "tus datos están a salvo", es "sabemos exactamente qué tenías y cómo volver a dejarlo igual". Restauración Semántica: test-restore va más allá de borg check . Mientras que otras herramientas solo verifican checksums (integridad técnica), nosotros verificamos si los datos son realmente legibles y útiles (integridad semántica): ¿el SQL de las BBDD se puede leer? ¿los paquetes están en formato válido? ¿las rutas esenciales existen? ¿los gzips no están corruptos? Asistente Guiado: restore-full y restore-dry-run forman un sistema de dos velocidades: simular antes de ejecutar, y guiar paso a paso durante la ejecución real. Esto reduce significativamente el "pánico" durante un desastre real, guiando incluso en la reinstalación de paquetes y fuentes APT. Resumen Este documento presenta el diseño, la implementación y la evaluación de backup.sh , un sistema de backup para Linux orientado a disco externo local. El sistema se basa en BorgBackup como motor de almacenamiento deduplicado, cifrado y comprimido. Se analizan las alternativas existentes (rsync, rsnapshot, restic), se justifican las decisiones de diseño y se presentan proyecciones de rendimiento basadas en métricas obtenidas de un sistema real con ~360 GB de datos, ~3200 paquetes instalados y ~460 paquetes instalados manualmente. Los resultados muestran que BorgBackup reduce el espacio de almacenamiento del backup completo a ~160 GB (55% de compresión con deduplicación), los backups incrementales se completan en 3-8 minutos, y el sistema permite r

2026-07-24 原文 →
AI 资讯

End-to-End Encryption and “Going Dark”

New paper: “ Encryption and Globalization 15 Years Later: End-to-End Encryption and the Third Round of the ‘Going Dark’ Debate “: Abstract : This Article updates and expands on 2012 research on encryption and globalization, analyzing what the authors call “Round 3” of the Going Dark Debate: the current controversies over end-to-end encryption (E2EE). Governments around the world have proposed, and in some cases enacted, laws limiting E2EE for law enforcement and national security purposes. This Article explains the underlying technologies and market developments for a law and policy audience to assess those proposals critically. The Article proceeds in three parts tracking three rounds of the Going Dark Debate. Round 1 covers the Crypto Wars of the 1990s, when U.S. export controls on strong encryption ultimately fell in 1999. Round 2 covers the period roughly 2010 to 2015, when encryption-in-transit became widespread but lawful access remained available through cloud providers, giving rise to what the authors called a “golden age of surveillance” rather than a period of going dark. Round 3 addresses the current debate over E2EE, where no entity between sender and recipient can read the plaintext...

2026-07-23 原文 →
AI 资讯

Pulling Business Rules Out of Your Service Isn't a Rewrite. It's a Seam.

Teams treat "externalize the rules" as two different decisions depending on the stack. In Node, it's "which npm package handles conditionals." In Java, it's "do we adopt Drools." Both framings are wrong in the same way — they turn an architecture decision into a product decision before anyone's actually designed the seam. The seam is the same regardless of language: rules become data instead of control flow, the boundary between your service and the rule layer is typed on both sides, and a contract test catches the moment a rule change would silently break what your code expects back. Get that seam right and it barely matters whether you're calling it from Express or Spring Boot. Get it wrong and you've just moved your if statements into a config file and called it progress. The seam: rules as data, not control flow The actual pattern is small. Instead of branching logic living inline in a handler or a service method, you define a typed input, a typed output, and a rule set that maps one to the other — evaluated somewhere the calling code doesn't need to know the internals of. That's it. That's the whole architectural move. Everything else — which engine, which language, how it's hosted — is an implementation detail on top of that seam. Most of the friction teams run into with a low-code layer in Node.js or a Java service isn't the rule engine choice. It's skipping the typed boundary and finding out three months later that a rule change silently returns a shape the calling code wasn't built to handle. Node.js: keeping the boundary typed Here's the seam in a TypeScript service. The route handler never sees a conditional — it sees a typed input going in and a typed decision coming out: interface PricingInput { userTier : " free " | " pro " | " enterprise " ; cartTotal : number ; couponCode ?: string ; } interface PricingDecision { discountPercent : number ; reason : string ; } async function evaluatePricingRule ( input : PricingInput ): Promise < PricingDecision > { c

2026-07-23 原文 →
AI 资讯

Unhandled Promise Rejections in Node.js: Why They Silently Kill Jobs

A background worker sits quietly for weeks, chewing through a job queue without incident, and then one Tuesday afternoon it disappears mid-batch, taking forty in-flight jobs down with it. No stack trace pointing at the offending line, no alert until a customer notices their export never finished. Nine times out of ten, the culprit is one of the most under-discussed failure modes in server-side JavaScript: unhandled promise rejections. They are easy to introduce, easy to miss in code review, and in modern Node.js they no longer just print a warning — they can end your process outright. This post walks through what unhandled promise rejections actually are at the engine level, why they are far more dangerous in long-running Node.js services than in typical web requests, how Node's default behavior around them has shifted over the years, and the concrete patterns you can use to stop them from quietly killing your jobs. What an Unhandled Promise Rejection Actually Is At the JavaScript engine level, a promise is a state machine with three possible states: pending, fulfilled, or rejected. When an async operation fails — a network call times out, a database query throws, a JSON parse blows up — the promise representing that operation transitions to the rejected state and carries a reason, usually an Error object. That's all a rejection is: a value flowing down the promise's error channel instead of its success channel. The engine only considers a rejection "handled" if, by the time it does its bookkeeping (which happens on a microtask checkpoint, not synchronously), there is a .catch() handler, a second argument to .then() , or a surrounding try/catch around an await somewhere in that promise's chain. If none of those exist anywhere in the chain, the rejection is classified as unhandled. This is the precise, narrow definition of unhandled promise rejections: a promise that reaches the rejected state with zero handlers attached anywhere along its chain of consumers. It's wo

2026-07-23 原文 →
AI 资讯

Build a Crypto Payment Support Desk

Most developers think about crypto payments as a checkout problem. Generate an invoice. Show a payment page. Wait for a webhook. Mark the order as paid. That is the clean version. Real merchants do not live in the clean version. They live in support tickets. A customer says they paid, but the order is still pending. A payment arrives after the invoice expires. Someone sends the right amount on the wrong network. A webhook fails. A customer underpays. A support agent cannot tell whether the issue is customer error, blockchain delay, invoice expiry, fulfillment failure, or an internal system bug. This is where developers can build a real product. A Crypto Payment Support Desk is a support and operations layer for merchants that accept crypto payments. It helps support teams search payments, inspect payment timelines, classify issues, explain statuses to customers, escalate real problems, and reduce the amount of manual investigation required for every crypto payment ticket. In this article, I will use OxaPay as the example payment infrastructure because its documentation exposes the primitives needed to build this kind of product: invoice generation, payment status callbacks, HMAC-signed webhooks, payment information lookup, payment history, static addresses, SDKs, plugins, and automation integrations. This is not a generic “add crypto payments to your app” article. It is a blueprint for developers who want to build a support-facing product that merchants may actually pay for. The business idea The idea is simple: Build a support desk that sits between a merchant's payment system, order system, and support team. The merchant already accepts crypto payments. The problem is that their support team cannot quickly answer payment-related questions. Your product gives them one place to investigate cases like: “The customer says they paid, but the order is unpaid.” “The invoice expired, but a transaction later appeared.” “The payment is underpaid.” “The webhook was received,

2026-07-23 原文 →
AI 资讯

Your API Retried the Request. Your Customer Got Charged Twice.

A customer clicks the Pay button, the server sends the request to a payment provider, and the charge succeeds. The trouble begins when the response never reaches the browser because the connection drops. The frontend sees a timeout and retries the same request, while the backend treats it as a brand-new payment. One customer action has now created two valid charges. This kind of failure is easy to miss because every part of the system appears to behave correctly on its own. The browser retries because it never received a response, the API processes a valid POST request, and the payment provider accepts the instruction it was given. The defect sits between those systems, where no component has enough context to know that the second request is a repeat. The same pattern can create duplicate orders, emails, subscriptions, shipments, support tickets, or background jobs. The common advice is to retry failed requests, but that advice is incomplete. A retry is safe only when the server can distinguish a repeated business operation from a new one. For payment APIs, that usually means introducing an idempotency key, storing the original result, and protecting the write path against race conditions. The rest of this article walks through that flow using Node.js, Express, and PostgreSQL. The bug starts with a normal-looking endpoint Consider a small Express endpoint that creates a payment by calling an external provider. The code receives the request body, sends the amount and customer data to the provider, and returns the resulting payment object. Nothing about this route looks obviously unsafe during a basic code review, and the happy-path test is likely to pass without trouble. The risk appears only when the request succeeds remotely but the response is lost before the client receives it. `app.post("/payments", async (req, res, next) => { try { const payment = await paymentProvider.charge({ customerId: req.body.customerId, amount: req.body.amount, currency: req.body.currenc

2026-07-22 原文 →
AI 资讯

Build a Crypto Payment Module for SaaS Apps

Most SaaS products do not need a “crypto payment button.” They need a payment module. That distinction matters. A button can redirect a customer to a payment page. A module has to know which user is paying, which workspace should be upgraded, which plan should become active, when access should expire, how failed or expired payments should be handled, how support can inspect payment status, and how finance can export records later. That is where developers can build a serious product. A Crypto Payment Module for SaaS Apps is a reusable layer that lets SaaS builders add crypto payments without building the whole payment lifecycle from scratch. In this article, I will use OxaPay as the example crypto payment infrastructure because its documentation exposes the primitives needed for this kind of module: invoice generation, white-label payments, static addresses, webhooks, payment information, payment history, SDKs for PHP, Python and Laravel, and automation integrations. This is not a “get rich with crypto APIs” article. It is a practical blueprint for developers who want to build something SaaS founders, indie hackers, agencies, and product teams may actually pay for. The core idea A Crypto Payment Module gives SaaS apps a production-ready way to accept crypto payments and translate payment events into SaaS account states. Instead of selling this: I can integrate crypto payments into your app. You sell this: I can give your SaaS a reusable crypto billing module with invoices, payment status tracking, webhook verification, plan activation, grace periods, admin tools, and payment history sync. That is a much stronger offer. A SaaS founder does not only care that a payment happened. They care that the right account is upgraded, the right plan is applied, the right billing period is extended, the right user sees the right status, and the support team can understand what happened when something goes wrong. That is what your module should solve. Why this is a real developer

2026-07-22 原文 →
AI 资讯

Teaching My Backend to Listen and Reply — FastAPI CRUD, Phase 2

Validate what comes in, shape what goes out, and give every outcome its proper status code. So Phase 1 gave my app a memory — a real database that remembers users and expenses even after a restart. Small problem: the only way to actually talk to it was a Python script I ran by hand. The app could remember things, but it was mute. Nobody on the internet could add an expense, list their spending, or delete a typo. Phase 2 fixes that. This is where the app grows a mouth — real HTTP endpoints you hit through the browser. The buzzword is CRUD : Create, Read, Update, Delete. The four things basically every app does to data. I went in thinking "it's just four functions, how hard can it be" and came out having learned about request/response contracts, dependency injection, and roughly six different HTTP status codes — mostly by triggering them wrong first. Let me dump what I learned [and the parts that tripped me up, because there were, uh, several]. The Structure is as follows. Lets call it PHASE 2 — The Endpoints: Set up the request/response contracts (two Pydantic schemas: one for input, one for output) Build a session dependency so every request gets a safe, auto-closing DB connection POST — create an expense GET — list them all + fetch one [with a proper 404] PATCH — update just the fields that changed DELETE — remove one cleanly First, the two new ideas [and no, nothing to install this time] Phase 1 had two new libraries. Phase 2 has two new ideas — and the nice part is there's nothing to pip install , because Pydantic already ships inside FastAPI. [Which means requirements.txt didn't change this phase. Still worth knowing the freeze habit is only for phases where you actually install something.] Pydantic — the bouncer at the door. Your SQLAlchemy models describe how data is stored ; Pydantic describes the shape data must have to cross the border of your API . It checks types, rejects garbage, and turns raw JSON into a clean Python object before it gets anywhere near

2026-07-22 原文 →
AI 资讯

REST API

I honestly thought learning REST APIs would be easy. At first, creating a simple GET or POST endpoint feels straightforward and you start thinking, "I've got this." Then reality hits. Every API needs middleware, validation, error handling, controllers, database integration, authentication, authorization, testing, pagination, CORS, environment variables, deployment and a dozen other things. Somewhere along the way, you realize you didn't just sign up to build an API—you signed up to build an entire backend ecosystem. 😂💻

2026-07-22 原文 →
AI 资讯

Why your Clio token stopped working

If your Clio integration started returning 401 and you are trying to work out why, the first question is not about your code. It is which of Clio's two OAuth systems you are on, because they have different rules and most advice on the internet does not say which one it is describing. There are two, and they are not interchangeable Clio Manage is the older one. OAuth at app.clio.com/oauth , API at app.clio.com/api/v4 . Clio Platform is the newer one, covering Grow and the lead inbox among others. OAuth at auth.api.clio.com/oauth , API at api.clio.com . They differ on essentially every point that matters when a token dies. Manage Platform Access token lifetime 2,592,000s, 30 days 86,400s, 24 hours Refresh token expiry Documented as none No time expiry, but rotates Refresh token rotation Not documented, and the refresh sample returns no new refresh token Documented: rotates on every use, previous one revoked Revocation endpoint POST app.clio.com/oauth/deauthorize , Bearer auth POST auth.api.clio.com/oauth/revoke , Basic auth Treat the lifetime row as the documented default rather than a constant. Honour the expires_in you get back on each response instead of hardcoding 30 days, because there are field reports of accounts issuing much shorter access tokens, and a hardcoded assumption fails in a way that looks exactly like revocation. That rotation row is the one that decides your debugging. On Platform , every refresh gives you a new refresh token and kills the old one, so failing to persist the new value out of each response leaves you holding a dead token the next time you try. Clio's docs say it directly: store the new refresh token returned in each response. On Manage , the documented behaviour is a long-lived refresh token that does not expire and is not replaced. Their refresh response sample does not even include a refresh_token field. So on Manage, a token that suddenly stops working usually points somewhere else: revocation, or the wrong region. The region trap

2026-07-22 原文 →
AI 资讯

How to apply a Clio task template to a matter through the API

There are two versions of this job and they have different answers. Most of the confusion, including ours, comes from assuming they are the same thing. Applying a whole template list to a matter Clio's interface has a button for this, and the natural assumption is that there is a matching endpoint on the task template resource. There is not. Nothing under /task_template_lists will assign anything to a matter, and no path in Clio's spec contains "apply". It is done from the matter instead. Both POST /matters.json and PATCH /matters/{id}.json accept a nested array: task_template_list_instances[] : task_template_list : { id } required on POST assignee_id : the user the list is assigned to notify_assignees : whether assignees get notified due_at : ISO-8601 date (format : date, not date-time) Those are the only two operations in the API that accept it. Note the asymmetry: on POST /matters.json the task_template_list object is required, and on PATCH /matters/{id}.json the item schema marks nothing as required at all. POST /task_template_lists/{id}/copy.json exists and sounds like the thing you want. It is not. It duplicates a list into another list , takes name , description and practice_area (all optional), and has no matter parameter. The two problems that will actually cost you time Instances are write-only. task_template_list_instances appears in those two request bodies and nowhere in the matter response schema. So there is no documented way to ask "does this matter already have the list on it?" You verify by fetching /tasks.json filtered to the matter and matching on names, which is uglier than it sounds and is most of the reconciliation work. Which makes idempotency your problem. If your automation fires the PATCH twice, nothing in the API stops you assigning the checklist twice. For anything triggered off matter creation or a stage change, decide up front how you detect an already-applied list, because the readback above is the only tool you have. Between them, th

2026-07-22 原文 →
AI 资讯

Test a Saga When Compensation Times Out and the Message Is Delivered Twice

A saga test that stops after “payment succeeded, inventory failed, refund called” assumes compensation is reliable. It is another distributed operation, so test it under the same faults. Use this sequence: 1. payment capture succeeds 2. inventory reservation times out 3. refund succeeds at provider 4. refund response is lost 5. compensation message is delivered again 6. late inventory-failed event arrives again The invariant is not “refund endpoint called once.” It is: captured amount - confirmed refunded amount = final charged amount and final charged amount is never negative Persist an inbox record for consumed message IDs and an outbox record for each intended side effect. Give the provider request a stable idempotency key derived from saga and compensation step: { "sagaId" : "order-42" , "step" : "refund-payment-v1" , "idempotencyKey" : "order-42:refund-payment:v1" , "amount" : 4900 } A deterministic simulator should permute duplicate delivery, delayed acknowledgement, worker crash, and out-of-order events. After every run, assert one terminal order state, at most one economic refund, and a complete evidence trail. “Already refunded” must reconcile to success only after amount and payment identity match. AWS describes coordination choices and rollback behavior in its Saga pattern guidance . The implementation detail that deserves its own test is durable compensation progress: a process restart cannot erase whether the external side effect occurred. Alert on sagas stuck in compensating , but do not let an operator click “retry” with a new key. The runbook should first query provider state, compare amount and currency, and resume the same operation identity. Exactly-once delivery is not required to preserve money. Stable operation identity plus reconciliation is.

2026-07-20 原文 →
AI 资讯

Does Prisma respect Supabase RLS? No — here's why

Prisma and Drizzle connect as the postgres role and bypass Supabase RLS entirely, so your policies never protect ORM queries. Here's the fix. TL;DR: No. Prisma and Drizzle open their own direct Postgres connection and log in as the postgres role, which owns your tables and carries BYPASSRLS — so Row Level Security is skipped on every ORM query. Point your app's connection at a dedicated, non-owner NOBYPASSRLS role (and keep the auth check in your code), not at postgres . If you built your Supabase project assuming RLS is a safety net on the data itself, adding an ORM quietly punches a hole straight through it. Your policies are still there. They just never run for the ORM's connection. Here's the mechanism, the myth to unlearn, and three fixes in order of how much you should reach for them. Does Prisma respect Supabase RLS? No. RLS is not a global property of the database — Postgres enforces it per role, per statement . A policy only bites for a role that is (a) not the table's owner, (b) has no BYPASSRLS attribute, and (c) is not a superuser. The Supabase JS client satisfies all three because it reaches Postgres through PostgREST, which runs your query as the unprivileged anon or authenticated role. Prisma and Drizzle satisfy none of them: they read DATABASE_URL and open a raw SQL connection as postgres , which owns virtually every table you migrated and holds BYPASSRLS . Either fact alone is enough for Postgres to skip your policies. So the same query that returns one tenant's rows through supabase-js returns every tenant's rows through Prisma. That is not a bug in your policy — it's the connection role. Two doors into the same database There are two completely different paths to your data, and they authenticate as different roles. The supabase-js path (RLS enforced). supabase-js talks HTTP to PostgREST, not to Postgres directly. PostgREST connects as authenticator , validates the request JWT, and does a SET ROLE into anon or authenticated for the statement. Those

2026-07-20 原文 →
AI 资讯

Syncthing File Sync for Self-Hosted Knowledge Systems

Syncthing keeps files synchronized across devices you control, making it one of the most practical tools for a self-hosted knowledge infrastructure that avoids cloud lock-in. Unlike cloud storage platforms, Syncthing uses a peer-to-peer model where each device holds its own copy of synced folders and exchanges changes directly with trusted peers. There is no central server that owns your data, no subscription account, and no vendor lock-in. The project is open-source and community-driven, with more details at syncthing.net . This architecture makes Syncthing especially useful for knowledge workers who manage markdown notes, research documents, PDFs, and project files across a desktop, laptop, home server, and possibly a phone. The tool is simple in concept but requires careful setup to avoid common pitfalls like treating sync as backup or syncing folders that should remain isolated. What Syncthing Is and Is Not Syncthing synchronizes files between two or more devices. Each device maintains its own copy of a folder, and changes propagate between trusted peers. Discovery and relay services may help devices find each other across networks, but the storage model remains local-first. The Syncthing documentation covers installation and configuration in detail. The calm but important opinion is this: Syncthing is excellent when treated as sync infrastructure. It becomes dangerous when treated as backup. It is not a cloud drive. It is not a complete backup system. It is not a collaboration suite. It is a private, peer-to-peer file synchronization tool. Why Syncthing Matters for Knowledge Management Knowledge management is not only about note-taking. It is also about where knowledge lives, how it moves, and whether it remains accessible over time — see the knowledge management guide for the broader picture of tools, methods, and self-hosted platforms this fits into. A useful personal or team knowledge system often contains: markdown notes PDFs and papers diagrams and screens

2026-07-19 原文 →