Skip to content

Smart Contract Authorization

NOOSChain smart-contract authorization is deliberately layered. A signed transaction proves who asked for a method to run. The contract instance defines which contract principal is executing. The manifest and bucket access rules then decide whether the requested operation is allowed.

This prevents a contract from becoming an accidental permission escalator.

Principals In A Contract Call

Every direct transaction CALL_CONTRACT has:

  • a caller, resolved from the signed transaction;
  • a contract actor, resolved from the deployed contract instance.

The contract actor is a first-class principal:

text
principalType: "contract"
principalId: <contract id>

Bucket access rules can grant permissions to that contract principal in the same way they grant permissions to users and organizations.

Nested contract calls, declared in Manifest Calls, add:

  • the root caller, carried from the original signed transaction;
  • the immediate caller contract;
  • the target contract.

The target method checks the immediate caller contract against its invoke.allow policy.

Invocation Policy

Each manifest method, described in Manifest Methods, has:

json
{
  "invoke": {
    "allow": [
      { "principalType": "organization", "principalId": "org-noos" }
    ]
  }
}

Supported invoke principals are:

PrincipalMeaningTypical use
anyUserAny authenticated user caller.Public read-style or low-risk methods.
anyContractAny deployed contract caller.Reviewed C2C extension points where all contracts may call.
anyAny authenticated user or any deployed contract caller.Intentionally public methods shared by users and contracts.
userOne specific user id.Personal/admin workflows.
organizationMembers or actors resolved to an organization.Team-scoped workflows.
contractOne specific calling contract id.Contract-to-contract callbacks.
systemSystem-level calls.Reserved platform workflows.

Use the narrowest policy that matches the method. Prefer explicit users, organizations, or contract callers for writes.

any is deliberately broad. Packaging and validation tooling warn when a method uses { "principalType": "any" } because it allows both human users and contracts. Use it only when the method is intentionally public to both caller classes. Prefer anyUser for user-facing public methods and anyContract for contract-only extension points.

Bucket Authorization

Bucket-sensitive host operations require three layers to line up:

text
method manifest declares the bucket and permission
AND contract principal has the bucket permission
AND caller has the bucket permission

The manifest declaration states what the method may attempt. The contract principal rule states what the deployed contract is allowed to access. The caller permission check prevents a caller from using a contract to reach a bucket the caller could not access directly.

For example, a method that writes report metadata might declare:

json
{
  "bucketId": "bucket-reports",
  "access": ["bucket:write"],
  "availability": "metadata"
}

The deployed contract then needs an access rule:

json
{
  "bucketId": "bucket-reports",
  "principalType": "contract",
  "principalId": "contract-report-store",
  "permissions": ["bucket:write"]
}

And the caller needs the required bucket permission through their own user or organization access rule.

Bucket Permissions

PermissionContract meaning
bucket:read_metadataMethod may read bucket consensus metadata.
bucket:read_encryptedMethod may read encrypted-record metadata and require local encrypted payload availability where declared. It does not reveal plaintext.
bucket:writeMethod may add encrypted-record metadata through the host layer.
bucket:adminAdministrative bucket capability; use sparingly for contract principals.

Host APIs never return plaintext or raw encrypted payload bytes to contract code. Encrypted payload availability is still controlled by bucket policy, local node authorization, and backfill.

Contract-To-Contract Authorization

Nested calls must pass checks on both sides.

The caller contract declares the outbound edge:

json
{
  "calls": [
    {
      "contractId": "contract-report-target",
      "methods": ["summarize"],
      "requiredBuckets": []
    }
  ]
}

The target method allows the caller contract:

json
{
  "invoke": {
    "allow": [
      { "principalType": "contract", "principalId": "contract-report-caller" }
    ]
  }
}

The runtime also enforces nested-call limits, fuel sharing, active/deactivated status, target manifest validity, and target readiness.

Readiness And Authorization

Readiness can fail before a call because the node can prove the deployed contract cannot satisfy its manifest locally. Common authorization-related readiness failures are:

  • a manifest references a missing bucket;
  • the contract principal lacks a declared bucket permission;
  • local encrypted payload availability is required but missing;
  • the manifest is invalid.

Check readiness before routing traffic:

powershell
npm run noos -- contracts readiness <CONTRACT_ID> --refresh --json

Common Failures

SymptomLikely causeFix
Caller cannot invoke method.Caller is not included in invoke.allow.Narrowly update the manifest and publish a new version, or use an allowed caller.
Bucket host call fails.Manifest lacks the bucket permission.Add the bucket requirement to the method manifest and republish.
Contract is not ready.Contract principal lacks bucket permission.Submit transaction ADD_BUCKET_ACCESS_RULE for principalType: "contract".
User can invoke but bucket write fails.Caller lacks bucket permission.Grant the caller or caller organization the required bucket permission.
Nested call fails.Caller manifest lacks calls, or target method does not allow the caller contract.Update caller and/or target manifest and publish compatible versions.

Design Rules

  • Keep write methods away from anyUser unless the bucket and business rules are intentionally public.
  • Treat any as broader than anyUser; it also allows contract callers and should trigger explicit review.
  • Grant contract principals only the bucket permissions each method needs.
  • Declare C2C edges explicitly and avoid dependency cycles.
  • Treat bucket admin grants to contracts as production-risk exceptions.
  • Review authorization together with the manifest hash, package provenance, and registry approval.

Next Steps

Audience-first NOOSChain documentation.