The day to day work of a developer is, of course, editing and testing code. In the world of ARGON, code is published as document entities.
Entry points
As discussed in the Entities page, entities contain references to code that provides the behaviour for that entitiy. That code is contained in software modules exposing a suitable interface.
There's a few kinds of code references an entity might make:
- A NEON user interface. The referenced code is loaded (inside a security sandbox) by the user agent software running in the NEON implementation on the client device, much like JavaScript code inside a browser.
- A CARBON dynamic knowledge gateway, triggered via a CARBON request coming in over the network. The referenced code is loaded by a cluster node providing compute services for the volume hosting the entity, and runs within the entity's security context.
- A MERCURY API request, triggered via a MERCURY request coming in over the network. The referenced code is loaded by a cluster node providing compute services for the volume hosting the entity, and runs within the entity's security context.
- A CAESIUM scheduled event, triggered via a schedule stored in the entity's state. The referenced code is loaded by a cluster node providing compute services for the volume hosting the entity, and runs within the entity's security context.
Either way, ARGON code is event-driven: procedures written to handle specific events. One never writes daemon code that sits there blocked waiting for something to happen; one never writes startup and shutdown event handlers that prepare "running state", as all state that persists between handlers is managed by the system. Different handlers for the same entity might be running at once on different nodes, and the system will handle distributed persistence, load balancing, and the like.
Personas
An entity ID always points to a specific entity in an ARGON cluster somewhere, but it also contains an optional "persona field" that allows a given entity to have an unlimited number of different entity IDs referring to it - exposing arbitrarily different behaviour.
MERCURY requests are routed to the same actual entity regardless of the persona field, but the contents of the persona field is made available to the entity when it handles the request. The persona field, if present, is protected by a cryptographic signature, granting the entity the ability to create entity IDs referring to itself with an arbitrary persona field (and denying that ability to third parties, although a third party can strip the persona field off an entity ID to get the ID of the physical entity).
Personas let an infinite number of "virtual" entities exist, while only one "real" entity needs to actually be created in the cluster. The entity just presents a different persona when accessed through a different ID. This makes it easy for entities to provide gateways to third-party systems by representing third-party objects as entities, embedding the identity of the third-party object in the persona field, for instance. Or an entity could do something like provide a distinct entity representation for every integer, by embedding an integer in the persona field, and present an interface that publishes various facts about that integer: its representation in Roman numerals, its prime factors, and so on - by computing them on the fly.
Entity context
A lot of the entry points mentioned above run in "the entity's security context". This gives the code access to:
- Request a copy of the entity's own ID, optionally with an arbitrary persona field.
- Access the entity's internal state, managed by TUNGSTEN.
- Make CARGON/MERCURY requests to other entities, either anonymously, or identified as the entity, or as a persona of this entity, or using a certificate to identify as something else, or using an arbitrary keypair as a pseudonym.
- Issue a time-limited signed certificate allowing the holder to identify requests as this entity or a persona of this entity.
- Access the entity's private WOLFRAM distributed cache
- Access WOLFRAM's distributed job execution framework
- All the usual runtime services available in the CHROME execution model
However, code may choose to run other code in a "sandboxed" dynamic context which relinquishes some of those rights, or introduces limits on space and time consumed by the computation.
Entity state
The state within an entity, managed by TUNGSTEN, is a number of named CARBON knowledge bases, known as "slices". The names can be any object, but are usually symbols or lists of symbols.
Names starting with a $ symbol are "well known" names, reserved by the ARGON system to have special meanings. They are:
- $MERCURY - a list of MERCURY interfaces this entity provides, each with an access control list and the name of the code module implementing that Interface, and a transit classification level. The entity admin interface is a special case; no code module is specified for this one as it's provided by the ARGON kernel on the node handling the request.
- $CAESIUM - a list of CAESIUM schedules this entity desires, containing the schedule settings (the next scheduled trigger time, rules for updating the trigger time if it's a repeating schedule, requests for how to catch up if triggers are missed), and of course the name of the code module handling that trigger.
- $CARBON - a list of the names of slices which are published directly via CARBON, with an access control list each, and a transit classification level.
The slices which are listed in $CARBON are "directly published"; when a "DOWNLOAD" CARBON request comes in, the contents of all slices which the access control lists deems visible are returned. However, as well as actual knowledge, those knowledge bases may also contain "gateways".
The DOWNLOAD request, as well as sending the contents of $CARBON, will also automatically append tuples declaring exposed interfaces declared in $MERCURY - and the hardcoded volume administration interface number -2, if the entity is a volume entity, unless it's already declared in $MERCURY. There's no need to hardcode in interface 0 (CARBON) or -1 (entity administration) as they are present on all entities, but if they are declared in $MERCURY in order to attach extra metadata, that will be included. These interface declarations contain the mapping from the interface symbol to the entity-specific interface number used to compactly represent the interface on the wire, and any public metadata provided in $MERCURY, including transit security requirements - so that clients can be advised of requirements, rather than having to send a request and have it rejected. Note that this metadata may also, optionally, be bundled in the EID returned by the "Access the entity's own ID" operation, for the same reason.
A gateway is a CARBON knowledge pattern with some variables in. When the gateway is encoded to send back in the original request, they are rewritten so the recipient just receives the knowledge pattern that forms the "head" of the gateway and an opaque signed token that forms the "tail"; they can request, via the CARBON protocol, to have the receiving node match a CARBON knowledge pattern matching the gateway's pattern and get back a list of matching results. The signed token contains a reference to the access control list entries that granted the caller access to find the gateway in the first place, and when a request comes in, the request is checked against those entries to ensure that the caller still has access to the gateway.
How the node then handles that request depends on the kind of gateway, as declared in the directly published slice.
One kind is a static gateway, which names a slice within the entity's state containing further knowledge exposed via CARBON, except remote entities don't get to download the whole lot; the incoming pattern is matched against knowledge in that slice, and the response returned. This enables an entity to contain a very large knowledge base and provide access to it, without letting/encouraging the world just download the whole lot. The same slice might be made available as a directly published slice via a suitable persona if bulk download is desirable as an option.
The other kind is a dynamic gateway, which names a code module that is invoked in the entity's security context and given the pattern, and returns a list of responses. This allows arbitrary code to answer CARBON queries.
But, what about personas? How can an entity ID with a persona field make an entity produce different behaviour? Well, when a CARBON or MERCURY (CAESIUM doesn't care about personas) request with a persona field comes in, different slices are consulted to find the behaviour. Although a persona field is defined in the entity ID format to be arbitrary data, in practice the proposed implementation will always store two pieces of information in there, protected by a cryptographic signature. The first piece of information is a symbol, the "persona scheme", and it is used as a prefix to the well-known slice names; MERCURY requests will be handled by the list stored in the slice whose name is a list with the persona scheme as the first element and $MERCURY as the second, and likewise with CARBON requests.
The second part of the persona, the "parameter", is arbitrary data and is provided as an optional argument (unset for non-persona requests) to any code invoked to handle CARBON dynamic gateways or MERCURY interface handlers.
Oh, and that cryptographic signature on the persona field, which must be valid for the request to be accepted? Well, there's a slice whose name is a list with the persona scheme as the first element and $CONFIGURATION as the second element which is consulted to find the secret used to check that signature - or to create the signature when minting an entity ID with a persona. If the slice or the keypair within doesn't exist when the ID is minted, one is created.
Debugging
So much of a programmer's time will be spent interacting with document entities containing source code, and interacting with prototype entities referencing that source code through an administrative interface that lets one edit their state directly. However, the administrative interface also lets one put an entity into debug mode.
When an entity is in debug mode, all handlers that are invoked on it appear in your debugging interface with an interface that lets you single-step through them, examining their state as you go, or letting it run until it hits a breakpoint, logging chosen kinds of activity (reads or writes of the persistent state, requests to other entities, etc) as it runs.
The language
ARGON supports using multiple different programming languages for handlers, but the default one is called CHROME. It's a purely functional language, but with uniqueness typing to manage mutation-esque operations in the manner of Concurrent Clean.
Largely, handlers are functions mapping the current state of the system and the details of the event being handled to a new state for the system. Operations on the state of the system would include reading and writing entity state, communicating with other entities (which includes, through gateways, communication with the Outside World of HTTP and such familiar protocols), and sending back a response to the event if one is needed.
Embedded development
There is one exception to the rule that all code lives within entity handlers. It's possible to write code that runs directly "in the kernel", which is useful for extending ARGON itself, or writing device drivers, or real-time tasks that have to respond to events within a bounded time interval too small and strict to allow disk access and competing with other handlers to get involved, or when writing code for minimal ARGON nodes that don't have the infrastructure required to host proper entities.
However, in such cases, your code is still edited inside an entity. But in this case you'll be telling the "node entity" representing the computer that's intended to run it to load it from the published module you're writing it in, and the debugger's a lot more primitive...