# ORILTO > Independent digital agency based in Bengaluru (Bangalore), India. Senior engineers building digital experiences, reliable systems, and useful AI for teams worldwide. ORILTO is the company. Orilto Workspace is its AI-native product, currently in private preview. The founder has 12+ years of coding experience. Senior teammates have prior experience at Meta, Microsoft and Amazon. These are prior employers, not client endorsements. ## Services - Product engineering: web and mobile apps, internal tools, full-stack digital products. - AI adoption: use-case discovery, agents and MCP, retrieval workflows and evaluations. - Systems and cloud: architecture, integrations, reliability, performance and modernisation. - Engineering partnerships: senior development capacity, technical audits and fractional leadership. ## Contact - Phone: +91-9304974821 - WhatsApp: https://wa.me/919304974821 - [Project enquiry](https://orilto.com/#contact) ## Product - [Orilto Workspace](https://workspace.orilto.com): projects, knowledge, relationships and inventory with people and AI agents on the same board, attributed changes and human review. Private preview. ## Engineering notes - [Your AI agent needs an authority model, not another prompt.](https://orilto.com/journal/ai-agents-need-an-authority-model/): Designing tool boundaries, review gates, and recoverable execution for AI that can change real systems. - [The request timed out. Did the work happen?](https://orilto.com/journal/designing-retries-with-a-transactional-outbox/): Idempotency, transactional outboxes, and the uncomfortable space between a failed response and a failed operation. - [Performance is a product behaviour, not a Lighthouse score.](https://orilto.com/journal/performance-is-a-product-behaviour/): A practical way to budget main-thread work, measure interaction latency, and keep rich interfaces responsive. ## Other pages - [Site index](https://orilto.com/sitemap/) - [Privacy](https://orilto.com/privacy/) - [Sitemap](https://orilto.com/sitemap.xml) - [RSS](https://orilto.com/feed.xml) Do not infer prices, delivery guarantees, client lists, availability or service-level commitments. Discuss engagement scope directly with ORILTO. # Your AI agent needs an authority model, not another prompt. ## Start with the effect An agent that drafts a plan and an agent that changes a customer record have different failure costs. Treating them as the same integration is an architecture mistake. Before choosing a model, describe the effects it may cause: create a task, update a document, send a message, deploy software, or move money. For each effect, define the actor, workspace, resource, allowed operation, and review requirement. The important unit is a business action. A tool named execute_sql hides its meaning from reviewers; a tool named propose_customer_merge makes the intended change visible. The MCP tools specification describes how a model discovers and calls tools, and recommends human visibility and control. Our architectural recommendation is to put enforcement behind the tool interface, in the same service that validates a human request. A model’s explanation is useful context; it is not an authorization decision. ## Separate intent from permission A helpful response might contain an instruction to “clean up inactive accounts.” That phrase is too broad to authorize a destructive operation. Resolve it into an exact proposal: which accounts, which expected versions, which action, and why each account qualifies. Then ask the policy layer whether this actor can perform that proposal. // Illustrative application contract type EffectProposal = { workspaceId: string; actorId: string; operation: 'archive_account'; resourceId: string; expectedVersion: number; reason: string; }; // The server supplies actor identity from authentication. // It checks scope and permissions again at execution. const decision = await authorize(actor, proposal); if (decision.requiresReview) return queueForReview(proposal); return executeWithVersionCheck(proposal); Never accept a model-supplied actor identifier as proof of identity. Bind authentication at the request boundary, and compare the requested workspace with the authenticated actor’s grants. A retrieved document, issue comment, or website can contribute facts. It cannot increase the agent’s permissions. Database isolation is another layer. PostgreSQL row security can restrict rows for ordinary roles, but owners and roles with bypass privileges need special consideration. The application still needs explicit authorization for operations such as sending messages or invoking a deployment provider. ## Review a concrete proposal A review screen should show the actual change a person is approving: destination, affected records, external recipients, and the before-and-after state. “Allow the agent to continue?” is a weak approval because the reviewer cannot bound its meaning. Store an immutable proposal and bind approval to its digest. If the agent changes the recipient or expands the record list, the previous approval no longer covers the new proposal. If a record version changes before execution, return a conflict and regenerate the proposal. Approval should not silently authorize overwriting someone else’s more recent work. Separate execution from acceptance. An agent may prepare a patch and attach evidence, but the same actor should not be able to manufacture independent review. Make the review state part of the domain model, rather than an optional instruction in a system prompt. ## Recover from uncertainty External calls introduce ambiguous outcomes. If a provider times out after accepting a request, retrying with a new identifier can create the effect twice. Persist the intended operation and reuse a stable business identifier on retries. When the provider supports idempotency, send that identifier with the request. When it does not, define a reconciliation path. Query the provider for a receipt or matching resource before issuing another write. Some outcomes must remain “unknown” until reconciliation finishes; changing that state to “failed” because a UI needs a tidy answer can lead to duplicate actions. Record the exact proposed effect before dispatch. Keep the provider receipt, effect identifier, and attempt history. Distinguish a rejected request from an uncertain provider outcome. Offer a human handoff when automatic reconciliation cannot establish the result. ## Evaluate the boundaries Evaluate more than answer quality. Build scenarios in which a document claims administrator permission, an agent requests a neighbouring workspace’s record, a tool response contains an instruction, and a provider response disappears after committing the effect. The system should reject unauthorized requests even when the model confidently asks for them. Measure useful completed work alongside incorrect effects, review burden, recovery time, and cost. A system that avoids all work is safe in a trivial sense but commercially useless. Start with one bounded workflow, understand its failure modes, and expand authority only when the evidence supports it. Practical starting point: give the first version read access and the ability to propose changes. Add execution permissions one business operation at a time. # The request timed out. Did the work happen? ## The ambiguous outcome Imagine an order API that writes a database row, publishes an event, and then returns a response. The client sees a timeout. Did the database commit? Did the broker receive the event? Did the response disappear on its way back? A timeout answers none of those questions. Retries are necessary because networks fail. The harder question is how to make a repeated request express the same intent. Our starting point is a stable request identity, a clearly defined transaction boundary, and a durable record of the work still owed. The AWS Builders’ Library discussion of idempotent APIs explains why callers need retry behaviour that preserves their intent. The design below is an illustrative order flow, not a report of a client deployment. ## Make intent identifiable Have the caller create an idempotency key before the first attempt. Scope the key to a tenant and operation, and persist a fingerprint of the normalized request. Reusing a key with a different payload should return a conflict. Otherwise a typo in the client can make an old operation appear to succeed with new arguments. CREATE TABLE request_receipts ( tenant_id uuid NOT NULL, operation text NOT NULL, request_key text NOT NULL, payload_hash text NOT NULL, resource_id uuid NOT NULL, response_body jsonb NOT NULL, PRIMARY KEY (tenant_id, operation, request_key) ); The uniqueness constraint is the concurrency primitive. Two application servers can receive the same request simultaneously, so a “read, then insert” check alone is insufficient. Within the business transaction, insert the receipt and resource, or handle the unique-key conflict and return the stored result after verifying the payload hash. Decide how long a key remains valid. If receipts expire before clients stop retrying, an old request can create a new effect. Key retention is part of the API contract and operational policy. ## One transaction, two records Writing to a database and publishing to a broker are independent operations. If publishing fails after commit, the event is missing. Publishing first creates the inverse problem: a consumer may see an event for a transaction that rolls back. A transactional outbox stores the business change and the pending event in the same database transaction. A separate dispatcher delivers committed outbox rows to the broker. This moves the unreliable boundary into a recoverable background process. BEGIN; -- Application supplies stable order_id and event_id. INSERT INTO orders (id, tenant_id, status) VALUES ($1, $2, 'placed'); INSERT INTO outbox (id, tenant_id, kind, payload) VALUES ($3, $2, 'order.placed', $4::jsonb); -- Persist request receipt in this same transaction. COMMIT; The outbox row should carry an event ID, aggregate ID, schema version, creation time, and enough payload to process the intended event. Avoid requiring a consumer to reconstruct an old event from mutable current state unless that is explicitly the desired contract. ## Delivery can still repeat A dispatcher may publish an event successfully and crash before marking it delivered. On recovery it will publish the event again. The outbox closes the lost-event gap at the database boundary; it does not make the entire distributed workflow exactly once. Make the consumer record the event ID and apply its local business effect in one transaction. A unique constraint on the consumed event ID prevents a repeated delivery from applying the local change twice. If processing calls another provider, propagate a stable business key or introduce a further durable dispatch record. Parallel dispatch also needs a claim or lease strategy, bounded retries, and an ordering decision. Order by aggregate when later events depend on earlier state. A global ordering guarantee is often more costly than the business needs. Every boundary should make its uncertainty visible enough to recover from. ## Operate the pending work Track the age of the oldest undelivered event, delivery attempts, lease expiry, and a dead-letter count. Queue length alone can look healthy while one important event remains stuck. Alert on the delay a business can tolerate, not only on infrastructure errors. Crash after the database commit and confirm dispatch resumes. Crash after broker acknowledgement and confirm duplicate delivery is harmless. Send the same key concurrently and verify one business resource exists. Reuse a key with a different payload and expect a conflict. Exercise poison events without blocking unrelated aggregates. The result is a system with an explicit recovery story. That is more useful than a success path that works beautifully until one response disappears. # Performance is a product behaviour, not a Lighthouse score. ## Measure the real task A page can load quickly and still feel slow when a person opens a menu, types into a search field, or submits a form. Initial loading is one part of the experience. The rest is a conversation between the user and a busy browser. Start with the critical user journeys: find a service, inspect the product, read an article, send an enquiry. Define what immediate feedback looks like at each step. A button that shows a pending state promptly can communicate progress even while the server does slower work. Interaction to Next Paint measures responsiveness across qualifying interactions. Treat it as a useful signal, then investigate the actual user task and device conditions behind the number. A synthetic lab run and field measurements answer different questions; neither should be mistaken for a universal guarantee. ## Account for main-thread work Rendering, event handlers, JavaScript evaluation, style recalculation, and layout compete for the main thread. Adding an animation does not just add download weight. It adds recurring work while the visitor is trying to interact. For a decorative particle scene, our design budget is explicit: cap pixel ratio, reduce particle count on small screens, stop when the scene leaves the viewport, stop when the document is hidden, and respect reduced-motion preferences. Avoid moving thousands of DOM nodes. A single canvas is a better representation for thousands of decorative particles. // A scheduler sketch, not a complete renderer. let frame = 0; let visible = true; let paused = false; function tick(now) { frame = 0; if (!visible || paused || document.hidden) return; renderScene(now); frame = requestAnimationFrame(tick); } function resume() { if (!frame && visible && !paused && !document.hidden) { frame = requestAnimationFrame(tick); } } This code needs lifecycle cleanup and visibility listeners in a real implementation. It also needs a static fallback when graphics initialization fails. The product’s content and contact paths should work independently of the renderer. ## Ship less work Rendering meaningful HTML on the server or at build time lets a browser show useful content before client scripts finish. A marketing page does not necessarily need a client-side application router, a global state store, or hydration for paragraphs and links. Keep the interactive enhancement separate. Load the 3D renderer only where the scene exists. Reserve dimensions for imagery so the layout stays stable. Host the font files you need and avoid loading an entire family for one tiny label. These choices remove work before any micro-optimization begins. A smaller bundle does not automatically mean better responsiveness. Parsing a compact script that performs expensive synchronous work can still block input. Inspect execution, style, and layout costs in a performance trace, then fix the largest relevant contributor. ## Instrument with purpose The PerformanceObserver API can expose supported performance entry types. Detect capabilities before observing them. Do not assume every browser produces the same entries. const supported = PerformanceObserver.supportedEntryTypes; if (supported.includes('longtask')) { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { recordTiming({ type: 'longtask', ms: entry.duration }); } }); observer.observe({ type: 'longtask', buffered: true }); } recordTiming represents your own privacy-conscious measurement pipeline. Send bounded timing data, not form contents or arbitrary DOM text. Long-task observation helps find blocking work, but it is not an INP implementation. Use a maintained Web Vitals implementation if you need the full metric, including its lifecycle rules. ## Test the experience Check a midrange phone, a narrow viewport, keyboard navigation, slow network conditions, and reduced motion. Test the page with scripts disabled. Submit an invalid form, simulate a server failure, and verify that a visitor can recover without losing their draft. Use a performance budget as a decision tool: what work is necessary for the user’s task, and what is the cost of the decoration? Keep visual richness where it helps the experience. Remove recurring work where it competes with the user. The goal is an interface that remains useful under imperfect conditions. Scores help diagnose it; the visitor’s ability to finish the task is the result.