From build 2025-07-04 you can now include comments in script. Comments will be treated as whitespace when the script is parsed. Relevant section from the reference documentation:
Whitepsace and Comments
Whitespace is ignored in script, except when it is used to separate tokens. This means that you can use whitespace to make your script more readable. Whitespace includes not only the space character, but other characters as well, such as the tab character, and new-line characters.
Comments in script are also considered whitespace, for the purpose of parsing. Comments can be written in two different ways: Either, by using the // character sequence, where all characters until the next newline character are considered whitespace, or by using the /* and */ character sequences, where all characters between the two sequences are considered whitespace. The second method allows you to create comments that span multiple lines.
For example, the following script is equivalent to the script a+b+c:
a + // First term
b + /* Second term
* is also added
*/
c // Third term
The free API package for interoperability with Microsoft products on neurons has been extended, to now support interoperability with Microsoft Excel documents also (saved using Open XML SDK, i.e. .xlsx documents). Conversion of Spreadsheets is also supported, to Script. This can be used for processing the information in the Spreadsheets, using scrfipt operations, etc. The Mictosoft Interoperability package is an optional component that can be installed by the neuron operator. The API is protected, and requires either a login (using user accounts defined by the Admin-interface; no special privileges), or a JWT token issued by the neuron (available to clients connected via XMPP, for instance), or being logged in through a web session. The API cannot be accessed anonymously.
Once the package has been properly installed, it will insert itself into the Script Prompt on the Neuron®. It introduces an Excel button that can be used to open an INPUT field that allows you to upload an Excel document to the server, and convert it to Script, on the page. This way, you can experiment with the conversion capabilities provided. If you want to have documents for testing purposes, the repository contains a set of testing documents.
Converting MultipleSheets.xlsx to Script
Internet Content Decoder
The package contains an Internet Content decoder for MS Excel documents. This means you can build your own web services, and similar tools, that receive Excel documents as content, and they will be automatically decoded. No automatic conversion to script is provided, as the script is supposed to be converted on the server, and not provided to the client, by default. You can convert the received Excel document using the static ExcelUtilities class on the server, or via the conversion API resource, if you request the script on the client.
Formatting Support
Microsoft Word supports a very rich set of features for formatting content in their documents. Markdown on the other hand, is not designed for creating formatted content in the same way, so a 1-to-1 conversion is not possible. But basic formatting constructs can be converted successfully. The following lists should give you an idea of what is supported and not.
What is exported?
As spreadsheets are exported to script, for processing purposes, formatting and Excel-specific formulas are not exported. Only values, and data types (such as the difference between strings, normalized (reused) strings, numbers, dates, times, etc.) are exported. Each sheet is exported as a Matrix, and a collection of sheets is exported as an object, where the tab names are used as property names, and the corresponding matrices as property values.
Logging of unrecognized elements
If converting an Excel document that contains elements the package library does not understand, information about these items will be logged to the event log. You can use this information to extend support to such items in the repository.
API Endpoint
API interface for the Excel -> Script conversion is very simple. You execute a POST to the /MicrosoftInterop/ExcelToScript resource on the Neuron® where the package is installed. The contents of the POST is the actual Excel document. The response will be Script, as text.
With the latest release of the TAG.XmppOpenAIBridge.package package (build time 2023-09-01) the ChatGPT OpenAI-integration supports function callbacks. You can both define functions the API can call via the API implementation, or via the script interface, as illustrated below. This makes it easier to integrate OpanAI functionality into your services, both .NET services hosted by the Neuron®, as well as Markdown and script-based services.
Custom function via chat
As an example of how to integrate custom functionality with ChatGPT, the XMPP <-> OpenAI bridge has added the following custom functions, to facilitate the presentation of non-text-based content:
ShowImage(Url[,Width[,Height[,Alt]]])
ShowImages(Images[])
ShowVideo(Url[,Title[,Width[,Height]]])
ShowYouTubeVideo(Url[,Title[,Width[,Height]]])
PlayAudio(Url[,Title])
ShareLink(Url[,Title])
ShareLinks(Links[])
An example conversation might look like:
Note: Test connections från lab.tagroot.io to ChatGPT have been removed, as test project has concluded. To use the XMPP<->OpenAI bridge, install the package above, and configure the service accordingly.
You can test ChatGPT via XMPP, by adding chatgpt@lab.tagroot.io to your contact list:
chatgpt@lab.tagroot.io
New script extensions
The following functions are available on systems with the TAG.XmppOpenAIBridge.package installed. A more detailed description about the OpenAI-related functions referenced in this section is available in the XmppOpenAIBridge repository.
Calls the chat completion API of OpenAI (ChatGPT). The Instruction argument contains initialization instructions. The optional Sender argument contains the JID of the sender. If not provided, the JID of the quick-login user will be used. Text is the chat message to send. Functions contains a single function definition or a vector of function definitions the API can call if it chooses to. History is a boolean parameter that indicates if the session history should be included in the query. The optional Preview argument indicates if intermediate content responses are previewed during the execution of the query. The response to the call will be an object containing a Content property with textual content, a Function property with function call information if available, including a Result property, containing any results from a function call. If a function call is requested, available function definitions or lambda expressions will be checked. If available, they will be called, with the arguments available from the API.
ChatGptConfigured()
Checks if Chat GPT is configured correctly. It requires a Chat GPT<->XMPP Bridge node to be configured in the MeteringTology source, with the Node ID ChatGPT.
Creates an array parameter for callback functions. The ItemParameter argument contains definition of each item in the array.
ChatGptBoolean(Name,Description,Required)
Creates a Boolean parameter for callback functions.
ChatGptEnum(Name,Description,Required,Values)
Creates an enumeration parameter for callback functions. The Values argument contains a vector of strings representing the possible values the argument can take.
ChatGptFunction(Name,Description,Parameters)
Creates a function definition for callback functions. The Parameters argument contains a vector of parameter definitions representing the arguments of the function.
Creates an integer parameter for callback functions, possibly within a specified range, between Min and Max, specifying also if the endpoints are included or not.
Creates an integer parameter for callback functions, possibly requiring it to be a multiple of a given base value, as well as within a specified range, between Min and Max, specifying also if the endpoints are included or not.
Creates a number (float-point) parameter for callback functions, possibly within a specified range, between Min and Max, specifying also if the endpoints are included or not.
Creates a number (float-point) parameter for callback functions, possibly requiring it to be a multiple of a given base value, as well as within a specified range, between Min and Max, specifying also if the endpoints are included or not.
Creates an object parameter for callback functions. The Properties argument contains a vector of parameter definitions representing the properties of the object.
Creates a string parameter for callback functions, having a regular expression to validate input.
ChatGptString(Name,Description,Required[,Format])
Creates a string parameter for callback functions, having a specific format, as given by the string format enumeration listed below.
Example:
ShowImage(Image):=
(
Get(Image.Url) ??? "Image not available"
);
ShowImages(Images):=
(
[foreach Image in Images do ShowImage(Image)]
);
R:=ChatGpt(
"You help users find images on the Internet, representative of the queries made by the user.",
"TestUser",
"Can you find me some images of Kermit? If something is unclear, ask for additional information first. When ready to present images to the user, call available functions.",
ChatGptFunction("ShowImages", "Displays an array of images to the user.", [
ChatGptArray("Images", "Array of images to show.", true,
ChatGptObject("Image", "Information about an image.", true, [
ChatGptString("Url", "URL to the image to show.", true),
ChatGptInteger("Width","Width of image, in pixels.", false, 0, false, null, false),
ChatGptInteger("Height","Height of image, in pixels.", false, 0, false, null, false),
ChatGptString("Alt", "Alternative textual description of image, in cases the image cannot be shown.", false)]))]),
false,
true);
R.Function.Result[0]
Result
Note: If running script with ChatGPT-services on a web server, you can use the associated script functions to push information asynchronously back to the web client using the PushEvent script function.
There are multiple ways for users to create broker accounts on a TAG Neuron®. With a broker account, a user can communicate on the federated XMPP network. They can also hold eDaler®, Neuro-Feature™ tokens, have legal identities and sign smart contracts, and use other related TAG services. Sometimes, a service provider using TAG Neurons must automate the creation of broker accounts, for instance, for integration with existing services. This article describes how this can be performed.
Account object
The TAG Neuron® uses an object database to store information it uses to operate accounts. So, to create an account, you need to create an account object. In script you use the Create function to create an object instance, from its type name. You can specify the type name, either using its local name, if there’s only one class with that name, or a qualified name or its fully qualified name. The fulle qualified name is the entire namespace + . + the local name. A qualified name is a portion of the fully qualified name that is uniquelly understood.
In the following example, the difference between the local name, a qualified name and the fully qualified name is shown. When there’s only one class matching a name, that type name is returned. If there are multiple classes that match the name, an array of type names is returned. (Example is done using the script prompt, so they should be read from bottom to top.)
Account class references
To avoid conflict in script over time (as new modules are added), fully qualified names are encouraged. To avoid making the code too cumbersome, you can assign namespaces and type names to variables. For instance, you can reference the Account class in the following manner:
Account reference
You can now create the new Account instance. Notice that above, you only referred to the type, while the result after this instruction is a newly created object instance. The output looks the same in this case, because the default ToString() method of the Account object simply returns the type name.
Creating Account instance
Account properties
You are now ready to set the properties of the object instance, for the new account. To know what properties are available, use the properties function:
Properties Function
With this information, you just set the properties according to your requirements, one at a time:
SettingProperties.png
Saving object
After setting the properties, the object is ready to be saved in the object database. As it is an object database, persistance of the object is done seamlessly. You do not need to know they underlying database structure or schema. All information required to persist the object, is available in the class definition itself. All you need to do, is call the SaveNewObject function. The function will return the saved object, which will now have an Object ID identifying the object in the object database.
SaveNewObject function
The account is now ready to be used.
Security Considerations
Below, are some security considerations, you need to be aware of.
Man-in-the-Middle attacks
It is recommended the user change the password as soon as possible. The password generated must be transmitted to the user somehow, and all transmission channels out-of-band should be considered vulnerable. A Man-in-the-middle (MITM) might hijack the password. With it, the MITM can do one of two things:
Choose to use the credentials invisibly, i.e. without changing it. This will allow them to gain access to sensitive material, until the password is changed. Once it has been changed, the old credentials are no longer usable, and the MITM can no longer access the account.
Try to alter the credentials before the real user does so, to gain access of the account. If so, the real user detect it on the next attempt to login. Through contact with support, account password can be reset and account given back to the real user. The faster the user changes the password, the better.
The risk here, is if account is not used by real user. A MITM would then gain access to the account, without it being noticed. One way to reduce the risk even further, is to transmitt parts of the password on different channels, hoping the MITM is not able to gain access to all of them. Still, it is wiser to assume credentials are compromized, as soon as they are transmitted out-of-band (meaning out of the scope of the encrypted protocol for which they are intended).
Entropy in passwords
The example above creates a 32-byte random string, that it BASE64-encodes into a usable password. This provides a security strength of 256 bits, which is usually higher than other protocols and algorithms provide, and so it should be sufficient for most use cases. However, if you decide to employ entropy checks on computer-generated passwords, you might need to adapt the password generation algorithm to filter out passwords that should be ignored, at the cost of reducing the security strength somewhat. See the article On entropy in passwords for more information about this.
Entropy can be seen as a measurement of the amount of randomness or chaos that exists in something. When it relates to passwords, it can be sean as a measurement of how difficiult it is to guess (using brute force). As computers become more powerful, and access to processors that can perform a huge amount of computations rapidly increases, it becomes more and more important to protect passwords and user credentials. this article shows some ways to increase the entropy in credentials used (to improve security), and therefore make it more difficult for malicious actors to access your system and information without you knowing or consenting.
Password-less solutions (Quick-Login)
It is important to realize humans are very poor random number generators (i.e. they are often very predictable). So, to avoid letting humans choose a password at all, maybe the best option to create a more secure system. One way to do this, is to use a device with a digital identity app, that allows you to sign in to a service. The app generates credentials in a much more secure manner, and can manage the signing in for the user, with a security strength beyond what human users normally can achieve. One such solution, is to use the TAG ID together with the Quick-Login feature of the TAG Neuron®. Benefits from this kind of solution includes:
Humans do not invent vulnerable passwords.
The service does not need to create a separate users database, with all the complexities of managing credentials, etc. The service gets the necessary information directly from the app, during quick-login.
You can use the same device/identity on multiple services on the federated Internet. This also means, different services can cooperate and interoperate, as the identity is the same.
Apps can use more methods to authenticate a person, than a web-page can. This includes usage of finger-prints, facial recognition, access to NFC or Bluetooth devices, etc., such as e-Passports or e-IDs, and so on.
Differentiate between local and network passwords
Sometimes, it is necessary to create a humanly-generated password. Understanding that such do not have sufficient entropy for use over the Internet, they should be restricted for local use only. One way to use such humanly-generated local passwords is to protect information in a secure storage on the device, and never use it in any communication setting, even in hashed form. The secure storage can then be used to store a much more secure password that is randomly generated using a cryptographic number generator, by the application itself. Once such a generated password can be securely stored and retrieved using the local password, it can be used for network communication purposes. The local password might be sufficiently strong to evade breach by a dedicated human local user, but not sufficiently strong to evade breach by dedicated malicious users on the Internet, especially if there are a lot of users using the same communication interfaces.
Encrease entropy in humanly created passwords
If you still need to provide human users with the ability to generate passwords and use them on the Internet, perhaps because of business, technology or knowledge boundaries, you must try to limit the possibilities of generating passwords that are too weak. One way to do this, is to enforce passwords to be of a certain length. But that is not sufficient, as a user who has chosen the password 123456 before, and is now forced to use 10 characters, would probably just use the password 1234567890. such a password would not be more secure, even if it’s longer. Another way to do this, is to count the number of occurrences of different character classes in the password, and force users to select passwords using characters from different classes. While this is annoying for the user, it increases the entropy of the passwords somewhat.
The following script example shows how to compute the number of occurrences of different character classes in a password, by using the .NET Char class, and its static methods to check individual characters in a string. (The code here assumes a character can belong to multiple classes.)
Computing character statistics
Check for randomness in computer-generated passwords
If a computer generates a password on the other hand, it will be a binary string, not necessarily representable by characters in a humanly-readable way. To solve this, random passwords are typically encoded using a limited alphabet, such as BASE64. Such passwords would be longer, have more entropy than a humanly generated password, but would fail a test based on character classes, as it would not use characters from a sufficient number of character classes. So a separate test of entropy needs to be performed on such passwords.
Assuming the password will be BASE64 encoded, the first step would be to decode the password into a binary string. Testing for entropy and randomness is a large field of study in itself, but we can perform a simple test on a proposed password, to check it is sufficiently random: We can create a histogram of the byte values in the password, and check that all buckets in the the result contain at least one value. This way, we know the bytes are distributed over the entire range of byte values, and a brute force-attacker must test for all possible values, greatly increasing the security strength of the solution. While this test does not catch passwords such as 0, 1, 2, 3, 4, 5, …, 255 (which has very little entropy), an app that generates such an insecure password is most probably developed to be wilfully insecure. And if the user (or developer) is wilfully insecure, they will be able to create an insecure password that passes any entropy test (as long as they know the methods to test the password).
Note: When performing a test using a histogram, the number of buckets to be used in the test is an important factor. For passwords based on 32 random bytes, 12 buckets will check for uniform distribution, and remove about half of generated passwords (see below). This means that accepted passwords would generate a security strength of 256-1=255, which is sufficently high, as other parts of the communication framework in a “normal” setting probably has security strengths ranging from 128 and up. (Note: security strengths are exponents of 2, so a security strength of 256 means a brute force attacker would require 2256 attempts to break a password. If half of passwords are removed, they need approximately 2256/2=2256-1=2255 attempts, i.e. the security strength is 255.)
The following script shows how such a randomness check could be implemented. (Here, the ??? operator is a short form of the traditional TRY ... CATCH ... operator. So if any of the function calls fail, the result will be false.
Checking for Uniform Distribution using a Histogram
Login auditor
While the above checks limit the use of unsecure passwords somewhat, malicious users (and robots made by malicious users) will still attempt to access the system continuiously, hoping to find a breach, at some point in time, on some server, for some user. Robots have patience, especially if funded by determined actors. The TAG Neuron® therefore also includes a Login Auditor, that if used monitors login attempts, and can block perceived malicious use, first temporarily, then permanently. Permanently blocked endpoints are furthermore logged in the Neuro-Ledger™, and distributed between Neurons, so that all neurons can benefit from knowing what endpoints to refuse. The auditor monitors these login-attempts, via logged events. It is therefore important to log successful and failed login attempts properly, so the login auditor can analyze them. Apart from distributing the results of blocks to other Neurons via the Neuro-Ledger™, different services running on the same Neuron®, or using different protocols, benefit from sharing this information. If you use the .NET framework for logging in a user in a Neuron®, such logging is performed automatically. All you need to do, is to check if there exists a temporary or permanent block, and present this information to the user, so they know when they can try again.
Tying it together
Let’s tie the above together, in an example that is implemented in a Markdown page on a TAG Neuron®. The page allows the currently logged in user to change its password. The user has just performed a POST to the page, the POST containing a JSON object, with three fields: CurrentPassword, Password and Password2. The contents has been provided to the page, which is currently being generated for the user, with the response. The processing of the request is embedded in script on the page itself. Using the function defined above, the first step is to check for randomness in the proposed password (and that the repeated password is equal to the first proposed password):
Entropy Checks
If the initial checks pass, we then continue to perform an actual login attempt, using the credentials provided. This login-procedure will check for blocks, and also appropriately log the attempt for the login auditor to analyze. The code then checks the results from the login attempt and presents the user with information what has been done. If successful, the password is changed. Information about the user is stored in an encypted database, but still, passwords are hashed, to avoid passwords from leaking by mismanagement of the contents of the database. Clearing any caches is also important, to make sure any cached user objects in any service are not used, and the new updated version is used instead.
Logging in, login audits and blocks
Reduction of security strength due to distribution check
As explained above, by refusing a number of randomly generated password, you reduce the security strength of the solution, as you reduce the number of available options of passwords. Empirically, we can easily measure how many passwords are removed using the parameters outlined above (i.e. using a 32-byte random password, and a historgram using 12 buckets to check for distribution). The test comes down to approximately 43% of generated passwords pass, and 57% of passwords are rejected, reducing the security strength from 32*8=256 to 255.