Adding human.json to WordPress

4 min read Original article ↗

Every few years, someone reinvents FOAF. The idea behind Friend-Of-A-Friend is that You can say "I, Alice, know and trust Bob". Bob can say "I know and trust Alice. I also know and trust Carl." That social graph can be navigated to help understand trust relationships.

Sometimes this is done with complex cryptography and involves key-signing ceremonies. Other times it involves byzantine XML RDF. Or you can use the baroque XHTML Friends Network.

None of those have been widely adopted. Perhaps it's because PGP is a usability nightmare, XML is out of fashion, or because these relationships mostly live in silos like Facebook and LinkedIn, or just that people value their privacy and don't want to expose their social graph any more than they have to.

Enter a new contender into the ring - human.json - it describes itself as:

a lightweight protocol for humans to assert authorship of their site content and vouch for the humanity of others. It uses URL ownership as identity, and trust propagates through a crawlable web of vouches between sites.

It looks like this:

 JSON{
  "version": "0.1.1",
  "url": "https://shkspr.mobi/blog/",
  "vouches": [
    {
      "url": "https://neilzone.co.uk/",
      "vouched_at": "2026-03-20"
    },
    {
      "url": "https://ohhelloana.blog/",
      "vouched_at": "2026-03-20"
    }
  ]
}

That says that I assert my own blog is written by a human, and that I vouch that my friends Neil and Ana write their own content.

Now, obviously there's no way that I can prove my blog posts are written by an organic, vegan-fed, human. And, while I know and trust the friends I've met AFK, I don't have any special insight into their creative processes. If I suspect them of being synthetic clankers, I can disavow their sites by removing them from my human.json file.

Adding to WordPress

There's an easy way and a hard way. The easy way it to just hand-write a JSON file and upload it to your website. BORING!

To start with, you'll need to add some code to your HTML's head. Stick this in your index.php

 HTML<link rel=human-json href=https://example.com/json/human.json>

Next, add this to your functions.php or wherever you set your weird options:

 PHP//  Add rewrite rule for /json and /json/{something}
add_action( "init", function () {
    add_rewrite_rule(
        '^json(?:/([^/]+))?/?$',    //  Matches /json and /json/{something}
        'index.php?pagename=json&json_param=$matches[1]',
        "top"
    );
});

//  Register custom query variable
add_filter( "query_vars" , function ($vars) {
    $vars[] = "json_param";
    return $vars;
});

That creates a rewrite so that /json/whatever will be intercepted. For now, this only deals with human.json - but there may be more weird JSON things you want to support later. Hurrah for over-engineering!

Next, add this:

 PHPadd_action( "template_redirect", function() {
    if ( get_query_var( "json_param" ) && "human.json" == get_query_var( "json_param" ) ) {
        $data = [
            "version" => "0.1.1",
                "url" => esc_url( home_url() ),
            "vouches" => [
                [
                           "url" => "https://friend.example.com",
                    "vouched_at" => "2026-03-20"
                ],
                [
                           "url" => "https://whatever.example",
                    "vouched_at" => "2026-03-20"
                ],

            ]
        ];
        //  Headers to make sure it all works.
        header( "Access-Control-Allow-Origin: *" );
        wp_send_json( $data, 200 );
    }
} );

That intercepts the request, generates some JSON, then serves it with the correct content type and CORS headers.

You may need to refresh your redirects. Easiest way is to go to your blog's admin page and choose Settings → Permalinks, then hit Save

Over over engineering

This takes a list of your human friends, deduplicates them, sorts them alphabetically, and changes the vouch date to that of when you last updated the files.

 PHPadd_action( "template_redirect", function() {
    if ( get_query_var( "json_param" ) ) {
        // https://codeberg.org/robida/human.json
        if ( strcasecmp( "human.json", get_query_var( "json_param" ) ) == 0 ) {

            //  People who I know to be human.
            $humans = array_unique([
                "https://neilzone.co.uk/",
                "https://ohhelloana.blog/",
                "https://example.com/",
            ]);

            sort( $humans );

            //  When was this file updated?
            //  RFC 3339 date format.
            $modified = date( "Y-m-d", filemtime( __FILE__ ) );

            foreach ( $humans as $human ) {
                $vouches[] = [ "url" => $human, "vouched_at" => $modified ];
            }

            $data = [
                "version" => "0.1.1",
                    "url" => esc_url( home_url() ),
                "vouches" => $vouches
            ];

            //  Headers to make sure it all works.
            header( "Access-Control-Allow-Origin: *" );
            wp_send_json( $data, 200, JSON_PRETTY_PRINT );
        } else {
            //  No valid parameter
            wp_send_json( null,  404, JSON_PRETTY_PRINT );
        }
    }
} );

Is it worth it?

I don't know.

Perhaps no one will use this. Or perhaps all my friends will turn out to be poorly constructed Turing machines. Or maybe a better standard will come along.

Either way, I think it is nifty and am happy to support it.

You can read more about human.json on CodeBerg.