npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ont-run

v0.0.16

Published

Ontology-enforced API framework for AI coding agents

Readme

ont-run

Vibe code with confidence.

A web framework designed for the era of coding agents. You define the ontology—what operations exist and who can perform them. AI writes the implementation.

https://github.com/user-attachments/assets/93fbf862-aca3-422d-8f0c-1fc1e4510d88

// ontology.config.ts
import { defineOntology, z } from 'ont-run';
import getTicket from './resolvers/getTicket.js';
import assignTicket from './resolvers/assignTicket.js';

export default defineOntology({
  name: 'support-desk',

  accessGroups: {
    public: { description: 'Unauthenticated users' },
    support: { description: 'Support agents' },
    admin: { description: 'Administrators' },
  },

  functions: {
    getTicket: {
      description: 'Get ticket details',
      access: ['support', 'admin'],
      entities: ['Ticket'],
      inputs: z.object({ ticketId: z.string().uuid() }),
      resolver: getTicket,
    },
    assignTicket: {
      description: 'Assign ticket to an agent',
      access: ['admin'],  // If AI tries to add 'public' here, review is triggered
      entities: ['Ticket'],
      inputs: z.object({ ticketId: z.string().uuid(), assignee: z.string() }),
      resolver: assignTicket,
    },
  },
  // ...
});

How It Works

Coding agents (Claude Code, Cursor, etc.) can edit resolver implementations freely. But changes to the ontology—functions, access groups, inputs—trigger a review that agents can't bypass. It's built into the framework.

| Layer | Contains | Who modifies | |-------|----------|--------------| | Ontology | Functions, access groups, inputs, entities | Humans only (via ont-run review) | | Implementation | Resolver code | AI agents freely |

Your job shifts: instead of writing every line, you encode what the system can do and who can do it. You review a visual map of capabilities—not 7,000 lines of code.

The Enforcement

If Claude tries to let public call assignTicket (currently admin-only):

  1. The ontology config changes
  2. ont.lock detects the mismatch
  3. Server refuses to start
  4. You review the visual diff and approve or reject
WARN  Ontology has changed:
      ~ assignTicket
        Access: [admin] -> [admin, public]

WARN  Run `npx ont-run review` to approve these changes.

Why not just prompts? The ontology is the source of truth. The framework won't run if it changes without human review.

Installation

npx ont-run init my-api

This creates a new project with the ont-run framework configured.

Quick Start

1. Initialize

npx ont-run init my-api
cd my-api

2. Review the initial ontology

npm run review

Opens a browser showing all functions and access groups. Click Approve to generate ont.lock.

3. Start the server

The generated project includes a full-stack setup with Vite + React Router for the frontend and Hono for the backend.

npm run dev

Your API is running at http://localhost:3000.

Writing Resolvers

Resolvers are where AI writes the implementation:

// resolvers/assignTicket.ts
import type { ResolverContext } from 'ont-run';

export default async function assignTicket(
  ctx: ResolverContext,
  args: { ticketId: string; assignee: string }
) {
  // AI can modify this freely—no review required
  const ticket = await db.tickets.update({
    where: { id: args.ticketId },
    data: { assigneeId: args.assignee },
  });

  return { id: ticket.id, assignedTo: args.assignee };
}

The resolver context provides:

  • ctx.env — Current environment name
  • ctx.envConfig — Environment configuration
  • ctx.logger — Logger instance
  • ctx.accessGroups — Access groups for the request

Configuration Reference

import { defineOntology, z } from 'ont-run';
import getUser from './resolvers/getUser.js';

export default defineOntology({
  name: 'my-api',

  environments: {
    dev: { debug: true },
    prod: { debug: false },
  },

  // Auth returns access groups (and optional user identity)
  auth: async (req: Request) => {
    const token = req.headers.get('Authorization');
    if (!token) return { groups: ['public'] };

    const user = await verifyToken(token);
    return {
      groups: user.isAdmin ? ['admin', 'user', 'public'] : ['user', 'public'],
      user: { id: user.id, email: user.email },  // Optional: for row-level access
    };
  },

  accessGroups: {
    public: { description: 'Unauthenticated users' },
    user: { description: 'Authenticated users' },
    admin: { description: 'Administrators' },
  },

  entities: {
    User: { description: 'A user account' },
    Ticket: { description: 'A support ticket' },
  },

  functions: {
    getUser: {
      description: 'Get user by ID',
      access: ['user', 'admin'],
      entities: ['User'],
      inputs: z.object({ userId: z.string().uuid() }),
      resolver: getUser,
    },
  },
});

Row-Level Access Control

The framework handles group-based access (user → group → function) out of the box. For row-level ownership (e.g., "users can only edit their own posts"), use userContext():

import { defineOntology, userContext, z } from 'ont-run';
import editPost from './resolvers/editPost.js';

export default defineOntology({
  // Auth must return user identity for userContext to work
  auth: async (req) => {
    const user = await verifyToken(req);
    return {
      groups: ['user'],
      user: { id: user.id, email: user.email },
    };
  },

  functions: {
    editPost: {
      description: 'Edit a post',
      access: ['user', 'admin'],
      entities: ['Post'],
      inputs: z.object({
        postId: z.string(),
        title: z.string(),
        // currentUser is injected at runtime, hidden from API callers
        currentUser: userContext(z.object({
          id: z.string(),
          email: z.string(),
        })),
      }),
      resolver: editPost,
    },
  },
});

In the resolver, you receive the typed user object:

// resolvers/editPost.ts
export default async function editPost(
  ctx: ResolverContext,
  args: { postId: string; title: string; currentUser: { id: string; email: string } }
) {
  const post = await db.posts.findById(args.postId);

  // Row-level check: only author or admin can edit
  if (args.currentUser.id !== post.authorId && !ctx.accessGroups.includes('admin')) {
    throw new Error('Not authorized to edit this post');
  }

  return db.posts.update(args.postId, { title: args.title });
}

Key points:

  • userContext() fields are injected from auth() result's user field
  • They're hidden from public API/MCP schemas (callers don't see or provide them)
  • They're type-safe in resolvers
  • The review UI shows a badge for functions using user context

The Lockfile

ont.lock is the enforcement mechanism. It contains a hash of your ontology:

What gets hashed (requires review):

  • Function names and descriptions
  • Access group assignments
  • Input schemas

What doesn't get hashed (AI can change freely):

  • Resolver implementations
  • Environment configurations
  • Auth function implementation

CLI Commands

npx ont-run init [dir]    # Initialize a new project
npx ont-run review        # Review and approve ontology changes

API Endpoints

All functions are exposed as POST endpoints:

# Call a function
curl -X POST http://localhost:3000/api/getUser \
  -H "Authorization: your-token" \
  -H "Content-Type: application/json" \
  -d '{"userId": "123e4567-e89b-12d3-a456-426614174000"}'

# List available functions
curl http://localhost:3000/api

# Health check
curl http://localhost:3000/health

The Bigger Picture

As the cost of software production trends toward the cost of compute, every business will encode itself as a software system—through autonomous agents and process orchestration.

ont-run is the enforcement layer that keeps AI agents aligned with your business rules as they automate your operations.

License

MIT