Agent as Platform User: Design It Right
Treat the agent as a first-class platform user. Add is_agent flag to your user model. Full RBAC. Complete audit trail. No bolt-ons. Integrated architecture.
The wrong way to build agent integration
Most teams bolt-on agent support. They have special code paths. If it is an agent, do X. If it is a user, do Y.
This scatters agent logic throughout the codebase:
Access control: special agent checks in permission middleware
Audit logging: separate agent log table instead of unified audit trail
Rate limiting: different limits for agents vs users
Notifications: agents do not get notified of actions
Every place that checks user identity needs to also check if it is an agent. Every access control decision duplicates logic. The codebase becomes messy.
The right way: agent as a user type
Treat the agent as a regular user with an is_agent flag.
Instead of special casing everywhere, you have:
One user model with is_agent: true/false
One RBAC system that works for agents and humans
One audit log for all actions
One identity layer
The agent is not special. It is just a user whose is_agent flag is true.
The step-by-step playbook
Step 1: Add is_agent flag to the user model
Modify your user schema:
User model (pseudo-schema): interface User {
id: string
email: string
name: string
is_agent: boolean // NEW
roles: Role[]
created_at: timestamp
updated_at: timestamp
}
That is it. One boolean field. No separate agent table. No bolt-on.
Step 2: Create agent users like regular users
When you deploy the Cloud Agent, create a user for it:
Agent user creation: const agentUser = await createUser({
email: 'agent@walnutai.internal',
name: 'WalnutAI Cloud Agent',
is_agent: true, // Flag it as agent
roles: ['test-generator', 'gap-analyzer'] // Standard roles
})
The agent gets standard roles. Roles determine permissions, just like for humans.
Step 3: Assign roles and permissions
Agents need specific permissions. Grant them via roles:
test-generator role: can read requirements, write test files
gap-analyzer role: can read code, write gap reports
defect-analyzer role: can read defects, write patterns
No special agent permissions. Same permission model. Agent just gets different roles than a typical engineer.
Step 4: Unified RBAC checks
Your access control middleware now checks one way for everyone:
RBAC check (pseudo-code): function canReadCode(user, codebase) {
if (!user.roles.includes('code-reader')) return false;
if (!userHasAccess(user.id, codebase.id)) return false;
return true;
}
One function. Works for agents and humans. No special cases.
Step 5: Unified audit trail
Every action gets logged to the same audit table:
Audit log entry: {
user_id: 'agent-123', // Agent user ID
is_agent: true,
action: 'generated-tests',
resource: 'story-456',
status: 'success',
timestamp: 2026-09-03T10:00:00Z,
details: { generated_count: 15, coverage: 0.96 }
}
Auditors can query agent actions just like user actions. One query. One schema. Complete visibility.
Why this matters
Security
No special agent code paths means no special security holes. Agents follow the same permission model as users.
If a user cannot read a codebase, the agent cannot either. By default.
Auditability
Compliance teams can audit agent actions using the same tools they use for user actions.
"Show me all actions on this codebase in the last month." Query returns both user actions and agent actions.
Maintainability
One RBAC system instead of two. One audit log instead of two. Less code to maintain. Fewer bugs.
Scalability
When you add more agents (or more user types), they integrate with the same model. No new code paths. Same architecture.
Common design patterns
Service accounts for agents
Create a special role: service-account. Agents get this role.
Service account users can only authenticate via API key or JWT, not passwords.
They automatically filter from any "list all users" queries so they do not clutter the UI.
Agent as read-only by default
Some agents should only read. Others should write. Model this in roles:
analyzer role: read-only (gap analysis, defect patterns)
generator role: read and write (test generation)
Scope limitations
Agents can be scoped to specific projects or teams:
Scoped agent user: const agent = await createUser({
email: 'agent@walnutai.internal',
is_agent: true,
roles: ['test-generator'],
scope: { project_id: 'project-123' } // Only this project
})
Agent can only access project-123 even if it has test-generator role globally.
What NOT to do
Do not create separate agent tables
Avoid: agents_table, agent_permissions, agent_audit_logs.
This duplicates logic and creates maintenance burden.
Do not special-case agent logic
Avoid: if (user.is_agent) { special logic }.
If agents need different behavior, model it in roles, not conditionals.
Do not skip audit logging for agents
Agents should be fully auditable. Their actions should appear in the audit trail.
"Agent did X" is a first-class audit event, just like "User did X".
Implementation checklist
Add is_agent boolean to user schema
Create service-account role for agents
Implement agent-specific roles (test-generator, gap-analyzer, etc.)
Update RBAC logic to work with is_agent flag (no special cases)
Update audit logging to capture is_agent
Create agent users for your Cloud Agent deployments
Test: agent can perform intended actions, blocked from restricted actions
Document: which roles agents need, which actions they can take
The payoff
Once you have agents as first-class users:
Adding new agents is trivial (create a user, assign roles)
Security is consistent (one permission model)
Audit is complete (one log, one query)
Code is cleaner (no special cases)
The extra hour spent on this architecture saves days of debugging down the road.
Next steps
Implement this pattern in your platform. Make agents first-class users from day one.
If you already have agent bolt-ons, refactor to this model. It is worth it.
Design agents as platform users. Integrate Cloud Agent cleanly.https://www.walnutai.ai/


