diff --git a/.gitignore b/.gitignore
index 6c172b698..5c94a8e1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,3 +70,4 @@ apps/splitwell/src/test/resources/splitwell-bundle*.tar.gz
**/SingletonCookie
__pycache__/
+docs/src/deployment/observability/metrics_reference.rst
diff --git a/LATEST_RELEASE b/LATEST_RELEASE
index 667843220..940ac09aa 100644
--- a/LATEST_RELEASE
+++ b/LATEST_RELEASE
@@ -1 +1 @@
-0.3.8
+0.3.9
diff --git a/VERSION b/VERSION
index 940ac09aa..5503126d5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.3.9
+0.3.10
diff --git a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DockerComposeValidatorFrontendIntegrationTest.scala b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DockerComposeValidatorFrontendIntegrationTest.scala
index 60bb02afa..c8b0a5fe9 100644
--- a/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DockerComposeValidatorFrontendIntegrationTest.scala
+++ b/apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/DockerComposeValidatorFrontendIntegrationTest.scala
@@ -1,5 +1,11 @@
package org.lfdecentralizedtrust.splice.integration.tests
+import com.digitalasset.canton.integration.BaseEnvironmentDefinition
+import org.apache.pekko.actor.ActorSystem
+import org.apache.pekko.http.scaladsl.Http
+import org.apache.pekko.http.scaladsl.client.RequestBuilding.Get
+import org.apache.pekko.http.scaladsl.model.StatusCodes
+import org.apache.pekko.http.scaladsl.model.headers.Host
import org.lfdecentralizedtrust.splice.environment.EnvironmentImpl
import org.lfdecentralizedtrust.splice.integration.EnvironmentDefinition
import org.lfdecentralizedtrust.splice.integration.tests.SpliceTests.SpliceTestConsoleEnvironment
@@ -8,13 +14,12 @@ import org.lfdecentralizedtrust.splice.util.{
FrontendLoginUtil,
WalletFrontendTestUtil,
}
-import com.digitalasset.canton.integration.BaseEnvironmentDefinition
+import java.lang.ProcessBuilder
+import java.nio.file.{Path, Paths}
import scala.concurrent.duration.*
import scala.jdk.CollectionConverters.*
import scala.sys.process.*
-import java.lang.ProcessBuilder
-import java.nio.file.{Path, Paths}
class DockerComposeValidatorFrontendIntegrationTest
extends FrontendIntegrationTest("frontend")
@@ -32,7 +37,7 @@ class DockerComposeValidatorFrontendIntegrationTest
extraClue: String = "",
startFlags: Seq[String] = Seq.empty,
extraEnv: Seq[(String, String)] = Seq.empty,
- ) = {
+ ): Unit = {
val command = (Seq(
"build-tools/splice-compose.sh",
"start",
@@ -246,6 +251,32 @@ class DockerComposeValidatorFrontendIntegrationTest
)
}
}
+
+ clue("validator and participant metrics work") {
+ implicit val sys: ActorSystem = env.actorSystem
+ registerHttpConnectionPoolsCleanup(env)
+
+ def metricsAreAvailableFor(node: String) = {
+ val result = Http()
+ .singleRequest(
+ Get(s"http://localhost/metrics")
+ // java can't resolve the *.localhost domain, so we need to set the Host header manually
+ .withHeaders(Host(s"$node.localhost"))
+ )
+ .futureValue
+ result.status should be(StatusCodes.OK)
+ result.entity.toStrict(10.seconds).futureValue.data.utf8String should include(
+ "target_info" // basic metric included by opentelemtry
+ )
+ }
+
+ metricsAreAvailableFor(
+ "validator"
+ )
+ metricsAreAvailableFor(
+ "participant"
+ )
+ }
}
}
diff --git a/apps/common/frontend/src/contexts/LedgerApiContext.tsx b/apps/common/frontend/src/contexts/LedgerApiContext.tsx
index 778439f85..11df48873 100644
--- a/apps/common/frontend/src/contexts/LedgerApiContext.tsx
+++ b/apps/common/frontend/src/contexts/LedgerApiContext.tsx
@@ -67,8 +67,15 @@ export class LedgerApiClient {
const response = await fetch(`${this.jsonApiUrl}v2/users/${encodeURIComponent(userId)}`, {
headers: this.headers,
});
- const responseBody = await response.json();
- return responseBody.user;
+ if (response.ok) {
+ const responseBody = await response.json();
+ return responseBody.user;
+ } else {
+ const responseBody = await response.text();
+ throw new Error(
+ `getPrimaryParty: HTTP ${response.status} ${response.statusText}: ${responseBody}`
+ );
+ }
},
this.userId
);
@@ -119,28 +126,27 @@ export class LedgerApiClient {
package_id_selection_preference: [],
};
+ const describeChoice = `Exercised choice: actAs=${JSON.stringify(
+ actAs
+ )}, readAs=${JSON.stringify(readAs)}, choiceName=${choice.choiceName}, templateId=${
+ choice.template().templateId
+ }, contractId=${contractId}`;
+
const responseBody = await fetch(
`${this.jsonApiUrl}v2/commands/submit-and-wait-for-transaction-tree`,
{ headers: this.headers, method: 'POST', body: JSON.stringify(body) }
)
- .then(r => {
- console.debug(
- `Exercised choice: actAs=${JSON.stringify(actAs)}, readAs=${JSON.stringify(
- readAs
- )}, choiceName=${choice.choiceName}, templateId=${
- choice.template().templateId
- }, contractId=${contractId} succeeded.`
- );
- return r.json();
+ .then(async r => {
+ if (r.ok) {
+ console.debug(`${describeChoice} succeeded.`);
+ return r.json();
+ } else {
+ const body = await r.text();
+ throw new Error(`HTTP ${r.status} ${r.statusText}: ${body}`);
+ }
})
.catch(e => {
- console.debug(
- `Exercised choice: actAs=${JSON.stringify(actAs)}, readAs=${JSON.stringify(
- readAs
- )}, choiceName=${choice.choiceName}, templateId=${
- choice.template().templateId
- }, contractId=${contractId} failed: ${JSON.stringify(e)}`
- );
+ console.debug(`${describeChoice} failed: ${JSON.stringify(e)}`);
throw e;
});
diff --git a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/store/DomainParamsStore.scala b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/store/DomainParamsStore.scala
index c7e63496d..a1a9c32b4 100644
--- a/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/store/DomainParamsStore.scala
+++ b/apps/common/src/main/scala/org/lfdecentralizedtrust/splice/store/DomainParamsStore.scala
@@ -142,7 +142,7 @@ object DomainParamsStore {
domainUnpausedPromise: Option[Promise[Unit]],
)
- private class Metrics(metricsFactory: LabeledMetricsFactory) extends AutoCloseable {
+ class Metrics(metricsFactory: LabeledMetricsFactory) extends AutoCloseable {
private val prefix: MetricName = SpliceMetrics.MetricsPrefix :+ "domain_params_store"
diff --git a/apps/common/src/test/scala/org/lfdecentralizedtrust/splice/store/db/SpliceDbTest.scala b/apps/common/src/test/scala/org/lfdecentralizedtrust/splice/store/db/SpliceDbTest.scala
index 65ed9e9fc..4f0bdebd6 100644
--- a/apps/common/src/test/scala/org/lfdecentralizedtrust/splice/store/db/SpliceDbTest.scala
+++ b/apps/common/src/test/scala/org/lfdecentralizedtrust/splice/store/db/SpliceDbTest.scala
@@ -106,14 +106,18 @@ trait SpliceDbTest extends DbTest with BeforeAndAfterAll { this: Suite =>
val dbLockPort: Int = 54321
implicit val tc: TraceContext = TraceContext.empty
logger.info("Acquiring SpliceDbTest lock")
- val lockTimeout = 10.minutes // expectation: Db tests won't take longer than 5m
+ // Needs to be long enough to allow all other concurrently started tests to finish,
+ // we therefore use a time roughly equal to the expected maximal duration of the entire CI job.
+ val lockTimeout = 20.minutes
dbLockSocket = BaseTest.eventually(lockTimeout)(
Try(new ServerSocket(dbLockPort))
.fold(
e => {
logger.debug(s"Acquiring SpliceDbTest lock: port $dbLockPort is in use")
throw new TestFailedException(
- s"Failed to acquire lock within timeout ($lockTimeout).",
+ s"Failed to acquire lock within timeout ($lockTimeout). " +
+ "We start many tests suites in parallel but wait for the lock before actually running test in this suite. " +
+ "Either increase the timeout, or reduce the number of test suites running in the same CI job.",
e,
0,
)
diff --git a/apps/metrics-docs/src/main/scala/org/lfdecentralizedtrust/splice/metrics/MetricsDocs.scala b/apps/metrics-docs/src/main/scala/org/lfdecentralizedtrust/splice/metrics/MetricsDocs.scala
new file mode 100644
index 000000000..39f08b24c
--- /dev/null
+++ b/apps/metrics-docs/src/main/scala/org/lfdecentralizedtrust/splice/metrics/MetricsDocs.scala
@@ -0,0 +1,111 @@
+// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfdecentralizedtrust.splice.metrics
+
+import better.files.File
+import com.daml.metrics.api.MetricsContext
+import com.digitalasset.canton.discard.Implicits.DiscardOps
+import com.digitalasset.canton.metrics.{MetricDoc, MetricsDocGenerator}
+import com.digitalasset.canton.topology.PartyId
+import org.lfdecentralizedtrust.splice.admin.api.client.DamlGrpcClientMetrics
+import org.lfdecentralizedtrust.splice.automation.TriggerMetrics
+import org.lfdecentralizedtrust.splice.scan.store.db.DbScanStoreMetrics
+import org.lfdecentralizedtrust.splice.sv.automation.singlesv.{SequencerPruningMetrics}
+import org.lfdecentralizedtrust.splice.sv.automation.ReportSvStatusMetricsExportTrigger
+import org.lfdecentralizedtrust.splice.sv.store.db.DbSvDsoStoreMetrics
+import org.lfdecentralizedtrust.splice.store.{DomainParamsStore, HistoryMetrics, StoreMetrics}
+import org.lfdecentralizedtrust.splice.wallet.metrics.AmuletMetrics
+
+final case class GeneratedMetrics(
+ common: List[MetricDoc.Item],
+ validator: List[MetricDoc.Item],
+ sv: List[MetricDoc.Item],
+ scan: List[MetricDoc.Item],
+) {
+ def render(): String =
+ Seq(
+ s"""|..
+ | Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+ |..
+ | SPDX-License-Identifier: Apache-2.0
+ |
+ |.. _metrics-reference:
+ |
+ |Metrics Reference
+ |=================
+ |""".stripMargin,
+ renderSection("Common", common),
+ renderSection("Validator", validator),
+ renderSection("SV", sv),
+ renderSection("Scan", scan),
+ ).mkString("\n")
+
+ def renderSection(prefix: String, metrics: List[MetricDoc.Item]): String = {
+ val header = s"$prefix Metrics"
+ (Seq(
+ header,
+ "+" * header.length,
+ ) ++
+ // We seem to automatically pull in the daml.cache metrics which make no sense for splice at this point
+ metrics.filter(m => !m.name.startsWith("daml.cache")).map(renderMetric(_))).mkString("\n")
+ }
+
+ def renderMetric(metric: MetricDoc.Item): String =
+ Seq(
+ metric.name,
+ "^" * metric.name.length,
+ s"* **Summary**: ${metric.summary}",
+ s"* **Description**: ${metric.description}",
+ s"* **Type**: ${metric.metricType}",
+ s"* **Qualification**: ${metric.qualification}",
+ "\n",
+ ).mkString("\n")
+}
+
+object MetricsDocs {
+ private def metricsDocs(): GeneratedMetrics = {
+ val walletUserParty = PartyId.tryFromProtoPrimitive("wallet_user::namespace")
+ val svParty = PartyId.tryFromProtoPrimitive("sv::namespace")
+ val generator = new MetricsDocGenerator()
+ // common
+ new DomainParamsStore.Metrics(generator)
+ new HistoryMetrics(generator)(MetricsContext.Empty)
+ new StoreMetrics(generator)(MetricsContext.Empty)
+ new DamlGrpcClientMetrics(generator, "")
+ new TriggerMetrics(generator)
+ val commonMetrics = generator.getAll()
+ generator.reset()
+ // validator
+ new AmuletMetrics(walletUserParty, generator)
+ val validatorMetrics = generator.getAll()
+ generator.reset()
+ // sv
+ new DbSvDsoStoreMetrics(generator)
+ new SequencerPruningMetrics(generator)
+ new ReportSvStatusMetricsExportTrigger.SvCometBftMetrics(generator)
+ new ReportSvStatusMetricsExportTrigger.SvStatusMetrics(
+ ReportSvStatusMetricsExportTrigger.SvId(svParty.toProtoPrimitive, "svName"),
+ generator,
+ )
+ val svMetrics = generator.getAll()
+ generator.reset()
+ // scan
+ new DbScanStoreMetrics(generator)
+ val scanMetrics = generator.getAll()
+ generator.reset()
+ GeneratedMetrics(
+ commonMetrics,
+ validatorMetrics,
+ svMetrics,
+ scanMetrics,
+ )
+ }
+
+ def main(args: Array[String]): Unit = {
+ val file = File(args(0))
+ file.parent.createDirectoryIfNotExists()
+ val docs = metricsDocs()
+ file.overwrite(docs.render()).discard[File]
+ }
+}
diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/ReportSvStatusMetricsExportTrigger.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/ReportSvStatusMetricsExportTrigger.scala
index 9c204d26b..caff46503 100644
--- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/ReportSvStatusMetricsExportTrigger.scala
+++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/ReportSvStatusMetricsExportTrigger.scala
@@ -151,9 +151,9 @@ class ReportSvStatusMetricsExportTrigger(
object ReportSvStatusMetricsExportTrigger {
- private case class SvId(svParty: String, svName: String)
+ case class SvId(svParty: String, svName: String)
- private case class SvCometBftMetrics(
+ case class SvCometBftMetrics(
metricsFactory: LabeledMetricsFactory
) extends AutoCloseable {
@@ -188,7 +188,7 @@ object ReportSvStatusMetricsExportTrigger {
}
}
- private case class SvStatusMetrics(
+ case class SvStatusMetrics(
svId: SvId,
metricsFactory: LabeledMetricsFactory,
) extends AutoCloseable {
diff --git a/build.sbt b/build.sbt
index 26ec38c59..377dfdc15 100644
--- a/build.sbt
+++ b/build.sbt
@@ -84,6 +84,7 @@ lazy val root: Project = (project in file("."))
`apps-splitwell`,
`apps-sv`,
`apps-app`,
+ `apps-metrics-docs`,
`apps-wallet`,
`apps-frontends`,
`splice-util-daml`,
@@ -186,7 +187,7 @@ lazy val docs = project
val srcDir = sourceDirectory.value
val log = streams.value.log
val cacheDir = streams.value.cacheDirectory
- val cache = FileFunction.cached(cacheDir) { _ =>
+ val cacheDamlDocs = FileFunction.cached(cacheDir) { _ =>
runCommand(
Seq("./gen-daml-docs.sh"),
log,
@@ -203,9 +204,30 @@ lazy val docs = project
(`splice-validator-lifecycle-daml` / Compile / damlBuild).value ++
(`splice-wallet-daml` / Compile / damlBuild).value ++
(`splice-wallet-payments-daml` / Compile / damlBuild).value
- cache(
+ cacheDamlDocs(
damlSources.toSet
).toSeq
+ import scala.sys.process._
+ val classPath = (`apps-metrics-docs` / Runtime / dependencyClasspath).value.files
+ val cacheMetricsDocs = FileFunction.cached(cacheDir) { _ =>
+ val metricsReferencePath = srcDir / "deployment" / "observability" / "metrics_reference.rst"
+ // This seems to be the easiest way to run a target from another SBT project and has the advantage
+ // that it is much faster than the approach taken by Canton of running the target from bundle with a console script.
+ runCommand(
+ Seq(
+ "java",
+ "-cp",
+ classPath.mkString(":"),
+ "org.lfdecentralizedtrust.splice.metrics.MetricsDocs",
+ metricsReferencePath.toString,
+ ),
+ log,
+ None,
+ Some(baseDir),
+ )
+ Set.empty
+ }
+ cacheMetricsDocs(Set()).toSeq
}.taskValue,
bundle := {
(Compile / resources).value
@@ -1317,7 +1339,20 @@ checkErrors := {
checkLogs("log/canton_network_test.clog", Seq("canton_network_test_log"))
}
-lazy val `apps-app` =
+lazy val `apps-metrics-docs` =
+ project
+ .in(file("apps/metrics-docs"))
+ .dependsOn(
+ `apps-common`,
+ `apps-scan`,
+ `apps-sv`,
+ `apps-validator`,
+ )
+ .settings(
+ Headers.ApacheDAHeaderSettings
+ )
+
+lazy val `apps-app`: Project =
project
.in(file("apps/app"))
.dependsOn(
@@ -1376,6 +1411,10 @@ printTests := {
def isNonDevNetTest(name: String): Boolean = name.contains("NonDevNet")
def isPreflightIntegrationTest(name: String): Boolean = name.contains("PreflightIntegrationTest")
+ def isIntegrationTest(name: String): Boolean =
+ name.contains("org.lfdecentralizedtrust.splice.integration.tests") || name.contains(
+ "IntegrationTest"
+ )
def isCoreDeploymentPreflightIntegrationTest(name: String): Boolean = isPreflightIntegrationTest(
name
) && !isValidator1DeploymentPreflightIntegrationTest(
@@ -1434,6 +1473,11 @@ printTests := {
// Order matters as each test is included in just one group, with the first match being used
val testSplitRules = Seq(
+ (
+ "Unit tests",
+ "test-full-class-names-non-integration.log",
+ (t: String) => !isIntegrationTest(t),
+ ),
(
"Daml ciupgrade vote",
"test-daml-ciupgrade-vote.log",
diff --git a/canton/community/app-base/src/main/scala/com/digitalasset/canton/metrics/MetricsDocGenerator.scala b/canton/community/app-base/src/main/scala/com/digitalasset/canton/metrics/MetricsDocGenerator.scala
new file mode 100644
index 000000000..3727962cc
--- /dev/null
+++ b/canton/community/app-base/src/main/scala/com/digitalasset/canton/metrics/MetricsDocGenerator.scala
@@ -0,0 +1,67 @@
+// Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package com.digitalasset.canton.metrics
+
+import com.daml.metrics.api.MetricHandle.{Gauge, LabeledMetricsFactory}
+import com.daml.metrics.api.noop.NoOpMetricsFactory
+import com.daml.metrics.api.{MetricHandle, MetricInfo, MetricsContext}
+
+import scala.collection.mutable.ListBuffer
+
+/** Fake labelled factory used to collect metrics */
+class MetricsDocGenerator extends LabeledMetricsFactory {
+
+ private val noop = NoOpMetricsFactory
+
+ def getAll(): List[MetricDoc.Item] = assembled.toList.map { case (metricType, info) =>
+ MetricDoc.Item(
+ name = info.name.toString(),
+ summary = info.summary,
+ description = info.description.stripMargin,
+ metricType = metricType,
+ qualification = info.qualification,
+ labelsWithDescription = info.labelsWithDescription,
+ )
+ }
+
+ def reset(): Unit = assembled.clear()
+
+ private val assembled = new ListBuffer[(String, MetricInfo)]()
+
+ override def timer(info: MetricInfo)(implicit context: MetricsContext): MetricHandle.Timer = {
+ assembled.addOne(("timer", info))
+ noop.timer(info)
+ }
+
+ override def gauge[T](info: MetricInfo, initial: T)(implicit
+ context: MetricsContext
+ ): MetricHandle.Gauge[T] = {
+ assembled.addOne(("gauge", info))
+ noop.gauge(info, initial)
+ }
+
+ override def gaugeWithSupplier[T](info: MetricInfo, gaugeSupplier: () => T)(implicit
+ context: MetricsContext
+ ): Gauge.CloseableGauge = {
+ assembled.addOne(("gauge", info))
+ noop.gaugeWithSupplier(info, gaugeSupplier)
+ }
+
+ override def meter(info: MetricInfo)(implicit context: MetricsContext): MetricHandle.Meter = {
+ assembled.addOne(("meter", info))
+ noop.meter(info)
+ }
+
+ override def counter(info: MetricInfo)(implicit context: MetricsContext): MetricHandle.Counter = {
+ assembled.addOne(("counter", info))
+ noop.counter(info)
+ }
+
+ override def histogram(info: MetricInfo)(implicit
+ context: MetricsContext
+ ): MetricHandle.Histogram = {
+ assembled.addOne(("histogram", info))
+ noop.histogram(info)
+ }
+}
diff --git a/canton/community/common/src/main/scala/com/digitalasset/canton/metrics/MetricDoc.scala b/canton/community/common/src/main/scala/com/digitalasset/canton/metrics/MetricDoc.scala
index fc10aada1..5bc6bd350 100644
--- a/canton/community/common/src/main/scala/com/digitalasset/canton/metrics/MetricDoc.scala
+++ b/canton/community/common/src/main/scala/com/digitalasset/canton/metrics/MetricDoc.scala
@@ -1,18 +1,19 @@
-// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+// Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.canton.metrics
-import com.daml.metrics.api.MetricDoc.Tag
import com.daml.metrics.api.MetricQualification
object MetricDoc {
final case class Item(
- tag: Tag,
name: String,
+ summary: String,
+ description: String,
metricType: String,
qualification: MetricQualification,
+ labelsWithDescription: Map[String, String],
)
}
diff --git a/cluster/compose/validator/nginx.conf b/cluster/compose/validator/nginx.conf
index 7513e491b..dcc2bbb3a 100644
--- a/cluster/compose/validator/nginx.conf
+++ b/cluster/compose/validator/nginx.conf
@@ -26,4 +26,20 @@ http {
proxy_pass http://ans-web-ui:80/;
}
}
+
+ server {
+ listen 80;
+ server_name validator.localhost;
+ location /metrics {
+ proxy_pass http://validator:10013/metrics;
+ }
+ }
+
+ server {
+ listen 80;
+ server_name participant.localhost;
+ location /metrics {
+ proxy_pass http://participant:10013/metrics;
+ }
+ }
}
diff --git a/cluster/images/splice-test-docker-runner/Dockerfile b/cluster/images/splice-test-docker-runner/Dockerfile
index a5c6904fd..5687d7fc5 100644
--- a/cluster/images/splice-test-docker-runner/Dockerfile
+++ b/cluster/images/splice-test-docker-runner/Dockerfile
@@ -1,5 +1,5 @@
# Note that we don't currently support arm64 runners, so we build this only for amd64
-FROM --platform=$BUILDPLATFORM ghcr.io/actions/actions-runner:2.321.0
+FROM --platform=$BUILDPLATFORM ghcr.io/actions/actions-runner:2.322.0
# TODO(#15988): can we reduce duplication between this and splice-test-ci ?
diff --git a/docs/src/app_dev/daml.rst b/docs/src/app_dev/daml.rst
deleted file mode 100644
index 19b998915..000000000
--- a/docs/src/app_dev/daml.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-..
- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-..
- SPDX-License-Identifier: Apache-2.0
-
-Daml API Reference
-==================
-
-API docs for daml packages
-
-
-.. toctree::
-
- api/splice-amulet/index
- api/splice-amulet-name-service/index
- api/splice-dso-governance/index
- api/splice-util/index
- api/splice-validator-lifecycle/index
- api/splice-wallet/index
- api/splice-wallet-payments/index
diff --git a/docs/src/app_dev/daml_api/index.rst b/docs/src/app_dev/daml_api/index.rst
new file mode 100644
index 000000000..06a2ca23b
--- /dev/null
+++ b/docs/src/app_dev/daml_api/index.rst
@@ -0,0 +1,22 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+.. _app_dev_daml_api:
+
+Daml API
+========
+
+API docs for daml packages
+
+
+.. toctree::
+
+ ../api/splice-amulet/index
+ ../api/splice-amulet-name-service/index
+ ../api/splice-dso-governance/index
+ ../api/splice-util/index
+ ../api/splice-validator-lifecycle/index
+ ../api/splice-wallet/index
+ ../api/splice-wallet-payments/index
diff --git a/docs/src/app_dev/ledger_api/index.rst b/docs/src/app_dev/ledger_api/index.rst
new file mode 100644
index 000000000..b95a43be3
--- /dev/null
+++ b/docs/src/app_dev/ledger_api/index.rst
@@ -0,0 +1,11 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+.. _app_dev_ledger_api:
+
+Ledger API
+==========
+
+.. todo:: Add content
diff --git a/docs/src/app_dev/index.rst b/docs/src/app_dev/overview/index.rst
similarity index 67%
rename from docs/src/app_dev/index.rst
rename to docs/src/app_dev/overview/index.rst
index bd2982b30..363a7bc88 100644
--- a/docs/src/app_dev/index.rst
+++ b/docs/src/app_dev/overview/index.rst
@@ -3,7 +3,7 @@
..
SPDX-License-Identifier: Apache-2.0
-
+.. _app_dev_overview:
Overview
========
@@ -22,34 +22,6 @@ Overview
.. todo:: add section on testing including spinning up localnet
.. todo:: add section on deployment for app devs, e.g., DAR uploads
-Version Information
-===================
-
-.. list-table::
- :header-rows: 0
-
- * - Canton version used for validator and SV nodes
- - |canton_version|
- * - Daml SDK version used to compile ``.dars``
- - |daml_sdk_version|
- * - Daml SDK version used for Java and TS codegens
- - |daml_sdk_tooling_version|
-
-Testing
-=======
-
-.. toctree::
-
- localnet
-
-
-API Reference
-=============
-
-.. todo::
-
- add overview over each API type, e.g., overview over scan
-
.. todo::
Add overview of how to integrate with CC at the Daml level
@@ -63,5 +35,5 @@ API Reference
.. toctree::
- splice_app_apis/index
- daml
+ version_information
+ splice_app_apis
diff --git a/docs/src/app_dev/splice_app_apis/index.rst b/docs/src/app_dev/overview/splice_app_apis.rst
similarity index 100%
rename from docs/src/app_dev/splice_app_apis/index.rst
rename to docs/src/app_dev/overview/splice_app_apis.rst
diff --git a/docs/src/app_dev/overview/version_information.rst b/docs/src/app_dev/overview/version_information.rst
new file mode 100644
index 000000000..e50e3223f
--- /dev/null
+++ b/docs/src/app_dev/overview/version_information.rst
@@ -0,0 +1,17 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+Version Information
+===================
+
+.. list-table::
+ :header-rows: 0
+
+ * - Canton version used for validator and SV nodes
+ - |canton_version|
+ * - Daml SDK version used to compile ``.dars``
+ - |daml_sdk_version|
+ * - Daml SDK version used for Java and TS codegens
+ - |daml_sdk_tooling_version|
diff --git a/docs/src/app_dev/scan_api/index.rst b/docs/src/app_dev/scan_api/index.rst
new file mode 100644
index 000000000..8ceca96ef
--- /dev/null
+++ b/docs/src/app_dev/scan_api/index.rst
@@ -0,0 +1,11 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+.. _app_dev_scan_api:
+
+Scan API
+========
+
+.. todo:: Add content
diff --git a/docs/src/app_dev/testing/index.rst b/docs/src/app_dev/testing/index.rst
new file mode 100644
index 000000000..b9540da85
--- /dev/null
+++ b/docs/src/app_dev/testing/index.rst
@@ -0,0 +1,15 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+.. _app_dev_testing:
+
+Testing
+=======
+
+.. todo:: Add content
+
+.. toctree::
+
+ localnet
diff --git a/docs/src/app_dev/localnet.rst b/docs/src/app_dev/testing/localnet.rst
similarity index 100%
rename from docs/src/app_dev/localnet.rst
rename to docs/src/app_dev/testing/localnet.rst
diff --git a/docs/src/app_dev/validator_api/index.rst b/docs/src/app_dev/validator_api/index.rst
new file mode 100644
index 000000000..fa89a2c61
--- /dev/null
+++ b/docs/src/app_dev/validator_api/index.rst
@@ -0,0 +1,11 @@
+..
+ Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
+..
+ SPDX-License-Identifier: Apache-2.0
+
+.. _app_dev_validator_api:
+
+Validator API
+=============
+
+.. todo:: Add content
diff --git a/docs/src/conf.py b/docs/src/conf.py
index c4470afc0..0487f7d89 100644
--- a/docs/src/conf.py
+++ b/docs/src/conf.py
@@ -132,8 +132,11 @@
.. |splice_cluster| replace:: :raw-html:`unknown_cluster`
-.. |da_hostname| replace:: :raw-html:`unknown_clusterglobal.canton.network.digitalasset.com`
-.. |gsf_sv_url| replace:: :raw-html:`https://sv.sv-1.unknown_clusterglobal.canton.network.sync.global`
+.. |da_hostname| replace:: :raw-html:`unknown_cluster.global.canton.network.digitalasset.com`
+.. |gsf_sv_url| replace:: :raw-html:`https://sv.sv-1.unknown_cluster.global.canton.network.sync.global`
+.. |generic_sv_url| replace:: :raw-html:`https://sv.sv-1.unknown_cluster.global.canton.network.YOUR_SV_SPONSOR`
+.. |gsf_scan_url| replace:: :raw-html:`https://scan.sv-1.unknown_cluster.global.canton.network.sync.global`
+.. |generic_scan_url| replace:: :raw-html:`https://scan.sv-1.unknown_cluster.global.canton.network.YOUR_SV_SPONSOR`
.. |version_literal| replace:: ``{version}``
.. |chart_version_literal| replace:: ``{chart_version}``
diff --git a/docs/src/index.rst b/docs/src/index.rst
index 7bac44de0..dfe579dc3 100644
--- a/docs/src/index.rst
+++ b/docs/src/index.rst
@@ -20,4 +20,15 @@ Splice Documentation
:maxdepth: 2
:caption: Application Developers
- app_dev/index.rst
+ app_dev/overview/index
+ app_dev/scan_api/index
+ app_dev/validator_api/index
+ app_dev/ledger_api/index
+ app_dev/daml_api/index
+ app_dev/testing/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Metrics Reference
+
+ deployment/observability/metrics_reference.rst
diff --git a/project/ignore-patterns/sbt-output.ignore.txt b/project/ignore-patterns/sbt-output.ignore.txt
index 0ecf84be3..21a80317c 100644
--- a/project/ignore-patterns/sbt-output.ignore.txt
+++ b/project/ignore-patterns/sbt-output.ignore.txt
@@ -116,4 +116,6 @@ WARNING: Failed to fetch secret, retrying in 10 seconds
# This is actually not a warning
Warning: No want/action statements, nothing to do
+.*The geckodriver version cannot be discovered.*
+
# Make sure to have a trailing newline