Skip to content

Our product

ClassProfile

A professional network scoped to a single organisation, for bodies that want a directory of their people rather than a page on somebody else's platform.

ClassProfile gives an organisation its own internal professional network: profiles with work history, education and skills, posts and comments, follows and connection requests, and visibility controls that run from public down to a single connection. Each organisation is an isolated tenant with owner, admin and member roles. Sign-up is open, there is no marketing site and no pricing, so it is an early product rather than a commercial one.

What it does

  • Organisations as isolated tenants, with owner, admin and member roles
  • Invitation tokens redeemed during registration
  • Profiles with headline, bio, skills, structured work history and education
  • Profile visibility: public, organisation, connections or private
  • Posts with their own visibility selector, editing and soft delete
  • Reactions, threaded comments, shares and bookmarks
  • Follows, connection requests, a following page and suggestions
  • Email and password sign-in, magic links, JWT sessions with refresh tokens
  • Combined search across people and posts within a tenant
  • Per-IP and per-user rate limiting, and a JSON data export

An alumni association, a professional body, a large training provider and a membership organisation all want roughly the same thing: a directory of their own people that behaves like a professional network. Almost all of them end up renting it. The public network owns the graph, advertises to the members, changes the rules when it suits itself, and keeps the data if the organisation leaves. That is a strange arrangement for what is, in most cases, the organisation's single most valuable asset.

Why it exists

ClassProfile is the smaller, self-contained version of that. An organisation is a tenant. Profiles, posts, search results and suggestions are scoped to it, so a member sees their own institution rather than the whole internet. People can also register without an organisation, in which case they join the untenanted pool of profiles and posts, which keeps sign-up from being a locked door for anybody who arrives before their organisation does.

The reason to build it rather than buy a forum is that the shape of the thing is a professional profile, not a thread. Members want work history, education, skills, a headline and a current role, and they want the person who looks them up to see something structured. A discussion tool with profile fields grafted on gets that backwards.

It is worth saying plainly what this product is not attempting. It is not a public network and it is not competing with one on scale. atHeartbeat is the other product in this portfolio with a timeline in it, and it goes the opposite way: open to anyone, no organisation, no walls. The two share almost no design decisions once you have chosen who is allowed in.

How it works

Registration is email, password and display name, followed by a verification link. Magic link sign-in and password reset both exist. Sessions use short-lived access tokens with refresh tokens, passwords are hashed with PBKDF2, and per-IP and per-user rate limits are held in a key-value store rather than in the database, so a burst of sign-in attempts is absorbed before it reaches the expensive layer.

Organisation membership comes from an invitation token redeemed during registration, with owner, admin and member roles. Membership is not inferred from email domain, which looks convenient right up until you meet the member who signed up with a personal address or the contractor who happens to have the right one.

Profiles carry a headline, bio, avatar, cover image, current position and company, location, website, a skills list, and structured work history and education entries. Posts have editing, soft delete, reactions, threaded comments, shares and bookmarks. Follows and connection requests run in parallel, with accept and decline, a following page and a suggestions page. Search runs over people and posts together. Media uploads go to an object storage bucket with a 50MB cap.

Visibility is two controls, deliberately kept apart.

Control Values The question it answers
Profile visibility public, organisation, connections, private Who can see who I am
Post visibility anyone, organisation only, connections only Who can see this thing I just wrote

A member can keep a quiet profile and still post to their organisation, or keep a public profile and put one particular post inside the walls. Merging the two controls in the name of simplicity means one of those two people cannot do what they want.

The decision that shaped it

The visibility model is four ordered levels rather than a page of switches, and that was the first real design argument. Twelve independent toggles produce 4,096 configurations, of which a test suite realistically covers a dozen near the defaults. More importantly, no user can hold 4,096 states in their head, so no user can predict what a stranger sees, and an organisation deciding whether to put its members' details into your product does not accept "there are settings for that" as an answer. Four sentences naming four groups of people is an answer. The full reasoning, including the code that enforces it, is in four visibility levels beat a page of toggles.

The ladder has a lie in it that had to be resolved. An ordered list implies each level nests inside the one above, but "in my organisation" and "connected to me" are overlapping sets, not rings. A member with a connection at a different organisation would otherwise clear the connections level and therefore the organisation level too, seeing content labelled organisation-only. So connection clearance also requires shared membership. The cost is that an outside connection sees less than the member might expect, which produces the occasional support message. The other direction produces an incident, and that is the whole basis of the choice.

The second decision was the data store. The model here is people, memberships, connections, follows and posts, which is a graph with a lot of joins in it, so it sits on a serverless PostgreSQL database rather than anything document-shaped. That keeps the queries honest and adds a vendor to the stack that the storage and rate-limiting layers do not share, which is a real operational cost for a product this small.

What we did not spend on is the feed. It is reverse chronological, capped at 50 posts, with no ranking at all. There are tables in the schema for feed events and per-user feed items and nothing reads them. That is intentional for now, because ranking is the part of a timeline that gets expensive fastest, and we have written up which parts of a timeline actually cost money rather than guess at it.

Where it stands today

Sign-up is open, the front end is live, the API is healthy and connected to a working database. There is no marketing page and no pricing page; the root path redirects to the feed and then to the login screen. This is an early product, and the list below is longer than we would like.

Messaging and notifications are not built. The database tables exist, the pages exist, and the pages say coming soon. There is no organisation admin dashboard and no user management screen, despite the README describing both; audit and security event tables exist in the schema with no views over them. Search is case-insensitive substring matching over display names, headlines and post content, not the ranked full-text search the README claims. Any rewrite of it has to carry the visibility rules across, which is what makes a search rewrite more expensive than it looks.

Three things are worse than incomplete and should be named as defects. The media upload endpoint returns a constructed public URL rather than a genuinely signed upload URL, and the code says so about itself, so uploads may simply not work in production; we have not tested that against the live API. The privacy consent, deletion request and cancel-deletion endpoints return success without persisting anything, and the data export returns account fields only rather than posts and profile data, which means a data protection promise is currently made by the interface and not kept by the code. And the About page carries the line "join thousands of professionals already using ClassProfile", which is placeholder copy sitting in the source with nothing behind it.

There is also no route for creating organisations, so tenants are set up by hand. The schema defines free, pro and enterprise tiers with per-tenant limits of ten users, 1GB of storage and 100 posts a month, plus a trial end date, and nothing in the code reads, enforces or charges against any of it. There is no payment provider anywhere in the repository. Nothing public exposes usage, so we cannot tell you how many people are on it.

What this says about how we work

The order of work here is the argument. Tenancy, the visibility model and the authentication path went first, and all three are painful to retrofit and invisible in a screenshot. Messaging is a screen with a list on it and can wait. A permission model cannot wait, because retrofitting one means auditing every endpoint that has ever rendered a person, and there are always more of those than anyone remembers: search, suggestions, mention autocomplete, notification text, exports, cached fragments.

The same page shows the failure mode of that order. Front-of-house work left undone for long enough turns into stubs that make promises, and a privacy endpoint returning success without doing anything is not a missing feature but a wrong answer given confidently. We would rather have that written on our own portfolio page than discovered by somebody else. A different flavour of the same isolation problem, solved with a database per customer instead of a tenant column, is in Consonas, and it is a useful comparison for anyone deciding how sealed their own product needs to be. If that is the decision in front of you, describe the situation to us and we will tell you which way we would go and what it would cost you.

What we wrote about building it

More of the same kind

Want something like this?

Tell us the shape of the problem and we will tell you honestly whether we are the right people for it.

Start a conversation