Dev.to · 10 min read

A Floor Beneath Every Person: Design Choices in the First Social Resource Floor Blueprint

A Floor Beneath Every Person: Design Choices in the First Social Resource Floor Blueprint

TL;DR — I've been building the Social Resource Floor: an open blueprint for coordinating one person's access to basic survival resources — food, housing, energy, healthcare, and more — across many independent providers, so that reaching those resources is grounded in being human rather than in financial access. The first blueprint version is now complete: language-neutral schemas, prose specifications, a reference implementation, and a first adapter. This post is about the engineering choices behind it, and the reasons for each — how it stays a contract rather than a product, how it keeps personal data out of the coordination layer, why it binds to existing standards instead of inventing new ones, and how I check that the contracts are implementation-independent rather than just claiming they are. The problem the Floor is trying to help with Today, for most people, survival routes through financial access. To reach food, housing, energy, or healthcare you generally need money, and to hold or move money you need banking, employment, or purchasing power. Financial access has become the gate standing in front of the resources a person needs to stay alive. The goal of the Social Resource Floor is narrow and specific: to help make it so that financial status is not the condition that determines whether a person can reach the basic resources required to survive. It does not try to abolish money, banks, or markets — money stays a first-class resource and delivery method. It aims at one thing: a floor beneath which no person should fall, defined locally, reachable regardless of financial circumstances. That's the mission. Everything technical below exists to make that mission buildable by the institutions — governments, municipalities, NGOs, cooperatives, community providers — that would actually run it, without asking any of them to give up their own systems or hand over their data. Where the Floor sits The delivery systems for social protection already exist and are strong: OpenSPP for orchestration, OpenG2P for delivery rails, OpenCRVS for civil registration. The Floor is not a replacement for any of them. It's a coordination layer above them, whose job is to let independent systems coordinate one person's whole survival floor — across many providers and many resource types at once — through shared, open interfaces, while each provider keeps its own mission and its own data. The whole design follows from that position. A layer that sits above independently-operated systems and coordinates without owning them has a specific set of constraints, and each of the choices below is an answer to one of them. Choice 1: Contracts first, implementation second The source of truth is a set of language-neutral JSON Schemas. Prose specifications describe their intent, a conformance suite validates against them, and a reference implementation demonstrates them — but nothing in the repository is permitted to depend on that reference implementation. Every dependency arrow points inward, toward the schemas: schemas/ source of truth — depends on nothing ^ ^ ^ | | | specification/ conformance/ reference/ + adapters/ (prose) (validates) (demonstrate, non-authoritative) The guiding rule is interface over implementation: the blueprint says what a component must be able to do, not which product must do it. The reason is adoption. If the reference implementation were the source of truth, then "conformant" would quietly come to mean "matches my code," and every participant would inherit my choices of language, database, and framework. Keeping the schemas authoritative and the reference deliberately non-authoritative means an independent party can build to the same contracts in a different stack and still interoperate. The concrete guardrail I hold myself to: if an adapter ever imports from the reference implementation, the dependency has inverted, and the project has started shipping a product instead of a contract. Choice 2: A small data spine The core data model is three nouns: A provider publishes resources. A resource is something a provider can supply — food, energy, housing, or money, which is one resource type among several rather than the organizing principle. An entitlement allocates a resource to a subject and names the provider responsible for fulfilling it. Around that spine sit six more schema areas — decision, provenance, event, consent, capability, and a public transparency report — for nine in total. Keeping the spine this small is deliberate: the fewer required concepts a participant has to adopt to join, the lower the barrier for a small community provider to become a full participant. Everything else is additive. Choice 3: Keep personal data out of the coordination layer A layer coordinating across many providers is a tempting place for personal data to accumulate, and that would be exactly the wrong outcome for a system meant to serve vulnerable people. So the design is data-minimizing by construction, not by policy. The mechanism is a single field, subject_ref: an opaque, pseudonymous handle that means "the same subject, consistently, across the documents that need to correlate" — and nothing more. It carries no name, no cleartext ID number, no readable date of birth. Resolving it into actual facts about a person is done by systems that already do that — civil registries, national ID systems, credential issuers — entirely outside the Floor. The Floor never makes the resolve() call itself; it only ever holds the opaque result. Identity is treated as an interface with zero provider lock-in, the same way policy is. This choice shows up throughout. The conformance suite resolves cross-references like a resource's provider_id or an entitlement's resource_id, but it deliberately never resolves subject_ref — there's nothing there to resolve, and that absence is the privacy property working as intended. Event envelopes, which are built to be broadcast between systems, carry the same discipline: subject_ref stays opaque and the payload carries coordination facts, never personal ones. Provenance records describe systems and operators, never subjects. A subject_ref is also not globally stable by default — a handle minted for one provider or jurisdiction need not be the same string used elsewhere for the same person, which limits how far any correlation can travel. Choice 4: Bind to what exists; invent as little as possible The most consistent decision across the specification is that before defining anything, I checked whether a mature standard or an existing system already solved it, and bound to that rather than inventing a parallel version. Each spec area records what was checked first. In practice that meant: Policy / eligibility. The Floor does not define a rules language. It defines the interface a policy engine must sit behind — evaluate(policy_id, policy_version, jurisdiction, subject_context, resource?) -> DecisionRecord — and names existing engines as candidate bindings: OpenFisca for legislation-as-code, CEL (which OpenSPP already uses for eligibility), and OPA/Rego for authorization-style rules. The decision record's engine.binding field just names which one produced a given outcome. Any of them can be swapped without changing anything downstream. Consent. Rather than a boolean flag on a subject's file, consent is modeled as records following the W3C Data Privacy Vocabulary and ISO/IEC TS 27560 — a status tied to a specific purpose, optionally narrowed to particular data categories or a particular provider/resource/decision. OpenSPP's own consent module is already DPV-aligned, so a participating deployment has something concrete to bind to. Identity. Bound to DCI's typed-identifier lookups, W3C Verifiable Credentials with OIDC4VCI, and OIDC/eSignet/Keycloak as candidate resolvers — none mandated, all external to the Floor. Authorization. The cross-provider "which system may do what" question binds to OAuth2 client-credential scopes, the machine-to-machine pattern OpenSPP's API and DCI already use. It's kept separate from OpenSPP's spp_user_roles RBAC, which solves a different problem — access control within one deployment, not between independent providers. Interoperability and capability discovery. These follow the pattern G2P Connect already uses in production: named, versioned, independently-adoptable interface codes, where a participant implements only the codes relevant to its role and advertises which ones before an exchange begins. The reason for this discipline is partly humility and partly durability: a coordination layer that reinvents consent, identity, and policy would be both arrogant about work others have done well and brittle against the systems it's meant to sit above. Binding to established interfaces means the Floor inherits their maturity and stays swappable as they evolve. Choice 5: Append-only records, and reproducibility Two smaller choices support auditability, which matters more than usual when the records decide what real people receive. Records that change are never edited in place. A consent withdrawal is a new record with status: withdrawn and a supersedes pointer to the record it revokes, producing an append-only history a subject or auditor can walk. Provenance corrections work the same way — a correction supersedes rather than silently overwrites. Decisions are built to be reproducible. policy_version is required on every decision record, because a decision that can't be re-evaluated against a fixed policy version can't be honestly audited later, whether for an appeal or a review. The same reasoning is why capability descriptors pin a schema_version: a version mismatch between two participants should be detectable before an exchange, not discovered as a validation failure after it. Choice 6: Federation, and capability-based participation The Floor is federated in the established sense — independent systems keeping control of their own operation and data, coordinating through shared open interfaces, in the pattern of email and the web where no one owns the protocol. It does not use or assume blockchain, ledgers, or tokens; "no single authority" refers to distributed governance, not to removing trusted parties, since providers, auditors, and operators remain trusted actors in accountable roles. Participation is capability-based: a participant declares and implements only the subset of areas relevant to its role. A small community provider might only publish resources and consume entitlements aimed at it; a national ministry might publish resources, decisions, and entitlements while consuming consent records. Both are full, conformant participants. This is what makes the structure scale-independent — one provider on its own is a valid deployment, and it grows into a union of many providers without changing shape. Checking that the contracts are implementation-independent A blueprint that says "anyone can implement these interfaces" is making a claim, and I wanted that claim to be testable rather than taken on trust. That's what the conformance suite is for. Fixtures are organized by intent: documents under valid/ must validate, and documents under invalid/ must be rejected, with each invalid fixture isolating a single violation so a failure points at one clear reason. The runner checks three layers, because the contracts are stricter than any single JSON Schema can express: Structural — each document is validated against its area's schema (JSON Schema 2020-12). Referential — cross-references between areas must resolve. A resource's provider_id, an entitlement's resource_id, provider_id, and decision_ref, and nested references like a consent record's scope.provider_ref all have to point at records that exist. subject_ref, as above, is intentionally excluded. Semantic — invariants the schema standard can't state. The public transparency report enforces a small-cell floor: a breakdown bucket can't report a count below a declared threshold, because small aggregates re-identify people. JSON Schema 2020-12 can't require one number to be at least as large as a sibling field, so the suite enforces it in code. The first blueprint version passes at 44 fixtures, 0 failures, and the full chain resolves end to end — a resource is published, a decision references it, an entitlement is drawn from it and pinned to the fulfilling provider — including a cross-provider case where a nationally-published resource is fulfilled by a different provider under the same shared floor. To check that "implementable by anyone" holds rather than just "implementable by my checker," there are two independent conformance runners over the same schemas and fixtures: the reference is Python with the jsonschema library, and the second is Node with ajv — a different language and a different validation engine, chosen so that agreement can't come from shared machinery. A small script runs both and compares their per-fixture verdicts: py="$(python3 conformance/runner/validate.py | verdicts)" node="$(cd implementations/node && node conformance.js | verdicts)" if [ "$py" == "$node" ]; then echo "cross-check: OK — both implementations agree on all $count fixtures" else diff

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News